50#include <QCoreApplication>
72,
r0(Eigen::Vector3f::Zero())
97 int fit_sphere_to_bem =
true;
106 qInfo(
"\nSetting up the BEM model using %s...", d->
bemname.toUtf8().constData());
107 qInfo(
"\nLoading surfaces...");
110 qInfo(
"Three-layer model surfaces loaded.");
115 qInfo(
"Homogeneous model surface loaded.");
118 qCritical(
"Cannot use a homogeneous model in EEG calculations.");
121 qInfo(
"\nLoading the solution matrix...");
124 qInfo(
"Employing the head->MRI coordinate transform with the BEM model.");
128 qInfo(
"BEM model %s is now set up", d->
bem_model->sol_name.toUtf8().constData());
132 if (fit_sphere_to_bem) {
134 float simplex_size = 2e-2f;
143 d->
r0 = r0_vec.head<3>();
146 qInfo(
"Fitted sphere model origin : %6.1f %6.1f %6.1f mm rad = %6.1f mm.",
147 1000 * d->
r0[0], 1000 * d->
r0[1], 1000 * d->
r0[2], 1000 *
R);
149 d->
bem_funcs = std::make_unique<dipoleFitFuncsRec>();
160 qInfo(
"Compensation setup done.");
162 qInfo(
"MEG solution matrix...");
165 if (d->
bem_model->fwd_bem_specify_coils(comp->comp_coils.get()) ==
FAIL)
175 qInfo(
"\tEEG solution matrix...");
185 qCritical(
"EEG sphere model not defined.");
188 d->
sphere_funcs = std::make_unique<dipoleFitFuncsRec>();
213 qInfo(
"Sphere model origin : %6.1f %6.1f %6.1f mm.",
214 1000 * d->
r0[0], 1000 * d->
r0[1], 1000 * d->
r0[2]);
255 Eigen::VectorXd stds;
259 qInfo(
"Using standard noise values "
260 "(MEG grad : %6.1f fT/cm MEG mag : %6.1f fT EEG : %6.1f uV)\n",
261 1e13 * grad_std, 1e15 * mag_std, 1e6 * eeg_std);
265 nchan = nchan + meg->
ncoil();
267 nchan = nchan + eeg->
ncoil();
273 for (k = 0; k < meg->
ncoil(); k++, n++) {
274 if (meg->
coils[k]->is_axial_coil()) {
275 stds[n] =
static_cast<double>(mag_std) * mag_std;
280 stds[n] = 1e6 * stds[n];
283 stds[n] =
static_cast<double>(grad_std) * grad_std;
288 for (k = 0; k < eeg->
ncoil(); k++, n++) {
289 stds[n] =
static_cast<double>(eeg_std) * eeg_std;
301 float nave_ratio =
static_cast<float>(f->
nave) /
static_cast<float>(
nave);
307 if (f->
noise->cov.size() > 0) {
308 qInfo(
"Decomposing the sensor noise covariance matrix...");
312 for (k = 0; k < f->
noise->ncov * (f->
noise->ncov + 1) / 2; k++)
313 f->
noise->cov[k] = nave_ratio * f->
noise->cov[k];
314 for (k = 0; k < f->
noise->ncov; k++) {
315 f->
noise->lambda[k] = nave_ratio * f->
noise->lambda[k];
316 if (f->
noise->lambda[k] < 0.0)
317 f->
noise->lambda[k] = 0.0;
322 for (k = 0; k < f->
noise->ncov; k++)
323 f->
noise->cov_diag[k] = nave_ratio * f->
noise->cov_diag[k];
324 qInfo(
"Decomposition not needed for a diagonal noise covariance matrix.");
328 qInfo(
"Effective nave is now %d",
nave);
337 float nave_ratio =
static_cast<float>(f->
nave) /
static_cast<float>(
nave);
345 if (f->
noise->cov.size() > 0) {
349 qInfo(
"Decomposing the noise covariance...");
350 if (f->
noise->cov.size() > 0) {
353 for (k = 0; k < f->
noise->ncov; k++) {
354 if (f->
noise->lambda[k] < 0.0)
355 f->
noise->lambda[k] = 0.0;
358 for (k = 0; k < f->
noise->ncov * (f->
noise->ncov + 1) / 2; k++)
359 f->
noise->cov[k] = nave_ratio * f->
noise->cov[k];
360 for (k = 0; k < f->
noise->ncov; k++) {
361 f->
noise->lambda[k] = nave_ratio * f->
noise->lambda[k];
362 if (f->
noise->lambda[k] < 0.0)
363 f->
noise->lambda[k] = 0.0;
368 for (k = 0; k < f->
noise->ncov; k++)
369 f->
noise->cov_diag[k] = nave_ratio * f->
noise->cov_diag[k];
370 qInfo(
"Decomposition not needed for a diagonal noise covariance matrix.");
374 qInfo(
"Effective nave is now %d",
nave);
414 float* w = wVec.data();
415 int nomit_meg, nomit_eeg,
nmeg,
neeg;
418 nomit_meg = nomit_eeg = 0;
425 bool selected =
false;
426 for (
int c = 0; c < meas->
nchan; c++) {
428 meas->
chs[c].ch_name,
429 Qt::CaseInsensitive) == 0) {
430 selected = sels[c] != 0;
445 if (
nmeg > 0 &&
nmeg - nomit_meg > 0 &&
nmeg - nomit_meg < min_nchan) {
446 qCritical(
"Too few MEG channels remaining");
449 if (
neeg > 0 &&
neeg - nomit_eeg > 0 &&
neeg - nomit_eeg < min_nchan) {
450 qCritical(
"Too few EEG channels remaining");
454 if (nomit_meg + nomit_eeg > 0) {
455 if (f->
noise->cov.size() > 0) {
456 for (j = 0; j < f->
noise->ncov; j++)
457 for (k = 0; k <= j; k++) {
461 for (j = 0; j < f->
noise->ncov; j++) {
462 f->
noise->cov_diag[j] *=
static_cast<double>(w[j]) * w[j];
481 const QString& measname,
486 const QString& badname,
487 const QString& noisename,
495 const QList<QString>& projnames,
499 auto res = std::make_unique<InvDipoleFitData>();
502 QStringList file_bads;
505 std::unique_ptr<MNECovMatrix> cov;
506 std::unique_ptr<FwdCoilSet> templates;
507 std::unique_ptr<MNECTFCompDataSet> comp_data;
508 std::unique_ptr<FwdCoilSet> comp_coils;
513 if (!mriname.isEmpty()) {
515 if (res->mri_head_t->isEmpty())
517 }
else if (!
bemname.isEmpty()) {
518 qWarning(
"Source of MRI / head transform required for the BEM model is missing");
521 float move[] = {0.0, 0.0, 0.0};
522 float rot[3][3] = {{1.0, 0.0, 0.0},
525 Eigen::Matrix3f rotMat;
526 rotMat << rot[0][0], rot[0][1], rot[0][2],
527 rot[1][0], rot[1][1], rot[1][2],
528 rot[2][0], rot[2][1], rot[2][2];
529 Eigen::Vector3f
moveVec = Eigen::Map<Eigen::Vector3f>(move);
533 res->mri_head_t->print();
535 if (res->meg_head_t->isEmpty())
537 res->meg_head_t->print();
541 if (!badname.isEmpty()) {
544 nbad = badlist.size();
545 qInfo(
"%d bad channels read from %s.", nbad, badname.toUtf8().data());
548 QFile measFile(measname);
550 if (measStream->open()) {
551 file_bads = measStream->read_bad_channels(measStream->dirtree());
552 file_nbad = file_bads.size();
557 if (badlist.isEmpty())
559 for (
int k = 0; k < file_nbad; k++) {
560 badlist.append(file_bads[k]);
564 qInfo(
"%d bad channels read from the data file.", file_nbad);
566 qInfo(
"%d bad channels total.", nbad);
580 qInfo(
"Will use %3d MEG channels from %s", res->nmeg, measname.toUtf8().data());
582 qInfo(
"Will use %3d EEG channels from %s", res->neeg, measname.toUtf8().data());
584 int nch_total = res->nmeg + res->neeg;
585 res->ch_names.clear();
586 for (
int i = 0; i < nch_total; i++)
587 res->ch_names.append(res->chs[i].ch_name);
603 QString qPath = QString(QCoreApplication::applicationDirPath() +
"/../resources/general/coilDefinitions/coil_def.dat");
605 if (!QCoreApplication::startingUp())
606 qPath = QCoreApplication::applicationDirPath() + QString(
"/../resources/general/coilDefinitions/coil_def.dat");
607 else if (!file.exists())
608 qPath =
"../resources/general/coilDefinitions/coil_def.dat";
610 QByteArray coilfileBytes = qPath.toUtf8();
611 const char* coilfile = coilfileBytes.constData();
621 Q_ASSERT(res->meg_head_t);
622 res->meg_coils = templates->create_meg_coils(res->chs,
632 qInfo(
"Head coordinate coil definitions created.");
651 if (comp_data->ncomp > 0) {
652 QList<FiffChInfo> comp_chs;
655 qInfo(
"%d compensation data sets in %s", comp_data->ncomp, measname.toUtf8().data());
657 QFile compFile(measname);
659 if (!compStream->open())
663 if (!compStream->read_meas_info(compStream->dirtree(), compInfo, compInfoNode)) {
668 for (
int k = 0; k < compInfo.
chs.size(); k++) {
670 comp_chs.append(compInfo.
chs[k]);
676 comp_coils = templates->create_meg_coils(comp_chs,
683 qInfo(
"%d compensation channels in %s", comp_coils->ncoil(), measname.toUtf8().data());
699 res->nmeg + res->neeg,
702 if (res->proj && res->proj->nitems > 0) {
703 qInfo(
"Final projection operator is:");
705 QTextStream errStream(stderr);
706 res->proj->report(errStream, QStringLiteral(
"\t"));
709 if (res->proj->assign_channels(res->ch_names, res->nmeg + res->neeg) ==
FAIL)
711 if (res->proj->make_proj() ==
FAIL)
714 qInfo(
"No projection will be applied to the data.");
719 if (!noisename.isEmpty()) {
722 qInfo(
"Read a %s noise-covariance matrix from %s",
723 cov->cov_diag.size() > 0 ?
"diagonal" :
"full", noisename.toUtf8().data());
725 if ((cov =
ad_hoc_noise(res->meg_coils.get(), res->eeg_els.get(), grad_std, mag_std, eeg_std)) ==
nullptr)
728 res->noise = cov->pick_chs_omit(res->ch_names,
729 res->nmeg + res->neeg,
736 qInfo(
"Picked appropriate channels from the noise-covariance matrix.");
742 if (res->proj && res->proj->nitems > 0 && res->proj->nvec > 0) {
743 if (res->proj->apply_cov(res->noise.get()) ==
FAIL)
745 qInfo(
"Projection applied to the covariance matrix.");
752 res->noise->revert_to_diag();
753 qInfo(
"Using only the main diagonal of the noise-covariance matrix.");
759 if (res->noise->cov.size() > 0) {
760 Eigen::Vector3f regs;
769 if (res->noise->classify_channels(res->chs,
770 res->nmeg + res->neeg) ==
FAIL)
776 for (
int k = 0; k < res->noise->ncov; k++) {
778 regs[res->noise->ch_class[k]] > 0.0)
785 res->noise->regularize(regs);
787 qInfo(
"No regularization applied to the noise-covariance matrix");
793 qInfo(
"Decomposing the noise covariance...");
794 if (res->noise->cov.size() > 0) {
795 if (res->noise->decompose_eigen() ==
FAIL)
797 qInfo(
"Eigenvalue decomposition done.");
798 for (
int k = 0; k < res->noise->ncov; k++) {
799 if (res->noise->lambda[k] < 0.0)
800 res->noise->lambda[k] = 0.0;
803 qInfo(
"Decomposition not needed for a diagonal covariance matrix.");
804 if (res->noise->add_inv() ==
FAIL)
809 return res.release();
815 const Eigen::Vector3f& Q,
822 const int nch = fit.
nmeg + fit.
neeg;
824 qWarning(
"print_fields: the data do not hold the %d fit channels", nch);
827 Eigen::VectorXf measured(nch);
829 qWarning(
"Cannot pick time: %7.1f ms", 1000 * time);
832 if (fit.
proj && fit.
proj->project_vector(measured,
true) ==
FAIL)
835 Eigen::MatrixXf fwd(nch, 3);
838 const Eigen::VectorXf predicted = fwd * Q;
840 for (
int k = 0; k < nch; ++k) {
841 const double scale = k < fit.
nmeg ? 1e15 : 1e6;
842 out << fit.
ch_names[k] <<
'\t' << scale * measured[k] <<
'\t' << scale * predicted[k] <<
'\n';
855 std::unique_ptr<InvDipoleForward> old)
857 std::unique_ptr<InvDipoleForward> res;
863 if (old && old->ndip == ndip && old->nch == d->
nmeg + d->
neeg) {
864 res = std::move(old);
866 res = std::make_unique<InvDipoleForward>();
869 res->fwd.resize(m, nch);
870 res->uu.resize(m, nch);
871 res->vv.resize(m, m);
873 res->scales.resize(m);
874 res->rd.resize(ndip, 3);
879 for (k = 0; k < ndip; k++) {
880 res->rd.row(k) = Eigen::Map<const Eigen::Vector3f>(rd[k]).transpose();
884 Eigen::MatrixXf this_fwd(d->
nmeg + d->
neeg, 3);
885 Eigen::Map<const Eigen::Vector3f> rd_k(rd[k]);
888 for (
int p = 0; p < 3; p++)
889 res->fwd.row(3 * k + p) = this_fwd.col(p).transpose();
895 for (
int p = 0; p < 3; p++)
896 S[p] = res->fwd.row(3 * k + p).squaredNorm();
898 for (
int p = 0; p < 3; p++)
899 res->scales[3 * k + p] = sqrt(
S[p]);
904 res->scales[3 * k + 0] = res->scales[3 * k + 1] = res->scales[3 * k + 2] = sqrt(
S[0] +
S[1] +
S[2]) / 3.0;
906 for (
int p = 0; p < 3; p++) {
907 if (res->scales[3 * k + p] > 0.0) {
908 res->scales[3 * k + p] = 1.0 / res->scales[3 * k + p];
909 res->fwd.row(3 * k + p) *= res->scales[3 * k + p];
911 res->scales[3 * k + p] = 1.0;
914 res->scales[3 * k] = 1.0;
915 res->scales[3 * k + 1] = 1.0;
916 res->scales[3 * k + 2] = 1.0;
928 int udim = std::min(m, n);
929 JacobiSVD<MatrixXf>
svd(res->fwd, ComputeFullU | ComputeFullV);
930 res->sing =
svd.singularValues();
931 res->uu =
svd.matrixV().transpose().topRows(udim);
932 res->vv =
svd.matrixU().transpose().topRows(udim);
944 const Eigen::Vector3f& rd,
945 std::unique_ptr<InvDipoleForward> old)
948 rds[0] =
const_cast<float*
>(rd.data());
957static float fit_eval(
const VectorXf& rd,
const void* user)
966 fwd = fuser->
fwd.get();
969 qInfo(
"ncomp = %d", ncomp);
971 Eigen::Map<const VectorXf> Bmap(fuser->
B, fwd->
nch);
972 for (c = 0, Bm2 = 0.0; c < ncomp; c++) {
973 one = fwd->
uu.row(c).dot(Bmap);
974 Bm2 = Bm2 + one * one;
976 return fuser->
B2 - Bm2;
982static int find_best_guess(
const Eigen::Ref<const Eigen::VectorXf>& B,
990 double B2, Bm2, this_good, one;
996 B2 = B.squaredNorm();
997 for (k = 0; k < guess->
nguess; k++) {
999 if (fwd->
nch == nch) {
1000 ncomp = fwd->
sing[2] / fwd->
sing[0] > limit ? 3 : 2;
1001 for (c = 0, Bm2 = 0.0; c < ncomp; c++) {
1002 one = fwd->
uu.row(c).dot(B);
1003 Bm2 = Bm2 + one * one;
1005 this_good = 1.0 - (B2 - Bm2) / B2;
1006 if (this_good > good) {
1013 qWarning(
"No reasonable initial guess found.");
1024static MatrixXf make_initial_dipole_simplex(
const Eigen::Vector3f& r0,
1033 float x = sqrt(3.0f) / 3.0f;
1034 float r = sqrt(6.0f) / 12.0f;
1037 float rr[][3] = {{x, 0.0f, -r},
1042 MatrixXf simplex = MatrixXf::Zero(4, 3);
1044 for (
int j = 0; j < 4; j++) {
1045 simplex.row(j) = Eigen::Map<const Vector3f>(rr[j]).transpose() * size + r0.transpose();
1050static bool dipole_report_func(
int loop,
1051 const VectorXf& fitpar,
1056 qInfo(
"loop %d rd %7.2f %7.2f %7.2f fval %g %g par diff %g",
1057 loop, 1000 * fitpar[0], 1000 * fitpar[1], 1000 * fitpar[2], fval_lo, fval_hi, 1000 * par_diff);
1066 const Eigen::Ref<const Eigen::VectorXf>& B,
1067 const Eigen::Vector3f& rd,
1080 ncomp = fwd->
sing[2] / fwd->
sing[0] > limit ? 3 : 2;
1083 for (c = 0, Bm2 = 0.0; c < ncomp; c++) {
1084 one = fwd->
uu.row(c).dot(B);
1085 Q += (one / fwd->
sing[c]) * fwd->
vv.row(c).head(3).transpose();
1086 Bm2 = Bm2 + one * one;
1091 for (c = 0; c < 3; c++)
1092 Q[c] = fwd->
scales[c] * Q[c];
1093 res = B.squaredNorm() - Bm2;
1114 Eigen::Ref<Eigen::VectorXf> B,
1121 float ftol[] = {1e-2f, 1e-2f};
1122 float atol[] = {0.2e-3f, 0.2e-3f};
1125 int max_eval = 1000;
1126 int report_interval = verbose ? 1 : -1;
1129 float good, final_val;
1130 Eigen::Vector3f rd_final, Q;
1132 int k, neval, neval_tot, nchan, ncomp;
1139 if (fit->
proj && fit->
proj->project_vector(B,
true) ==
FAIL)
1142 if (fit->
noise->whiten_vector(B, B, nchan) ==
FAIL)
1147 if (find_best_guess(B, nchan, guess, limit, best, good) < 0)
1152 user.B2 = B.squaredNorm();
1154 user.report_dim =
false;
1157 rd_guess = guess->
rr.row(best).transpose();
1158 rd_final = rd_guess;
1162 for (k = 0; k < ntol; k++) {
1173 MatrixXf simplexMat = make_initial_dipole_simplex(rd_guess, size);
1174 for (
int p = 0; p < 4; p++)
1175 vals[p] = fit_eval(simplexMat.row(p), fit);
1178 auto cost = [fit](
const VectorXf& x) ->
float {
1179 return fit_eval(x, fit);
1191 dipole_report_func)) {
1195 float rv = 2.0f * (vals.maxCoeff() - vals.minCoeff()) / (vals.maxCoeff() + vals.minCoeff());
1196 qWarning(
"Warning (t = %8.1f ms) : g = %6.1f %% final val = %7.3f rtol = %f",
1197 1000 * time, 100 * (1 - vals[0] /
user.B2), vals[0], rv);
1201 rd_final = simplexMat.row(0).transpose();
1202 rd_guess = simplexMat.row(0).transpose();
1205 final_val = vals[0];
1213 if (fit_Q(fit, B, rd_final,
user.limit, Q, ncomp, final_val) ==
OK) {
1218 res.
good = 1.0 - final_val /
user.B2;
1221 res.
khi2 = final_val;
1223 res.
nfree = nchan - 3 - ncomp - fit->
proj->nvec;
1225 res.
nfree = nchan - 3 - ncomp;
1226 res.
neval = neval_tot;
1243 static const Eigen::Vector3f Qx(1.0f, 0.0f, 0.0f);
1244 static const Eigen::Vector3f Qy(0.0f, 1.0f, 0.0f);
1245 static const Eigen::Vector3f Qz(0.0f, 0.0f, 1.0f);
1258 Eigen::MatrixXf vec_meg(3,
nmeg);
1261 fwd.topRows(
nmeg) = vec_meg.transpose();
1263 auto fwd0 = fwd.col(0).head(
nmeg);
1264 auto fwd1 = fwd.col(1).head(
nmeg);
1265 auto fwd2 = fwd.col(2).head(
nmeg);
1282 Eigen::MatrixXf vec_eeg(3,
neeg);
1285 fwd.block(d.
nmeg, 0,
neeg, 3) = vec_eeg.transpose();
1287 auto fwd0 = fwd.col(0).segment(d.
nmeg,
neeg);
1288 auto fwd1 = fwd.col(1).segment(d.
nmeg,
neeg);
1289 auto fwd2 = fwd.col(2).segment(d.
nmeg,
neeg);
1302 for (k = 0; k < 3; k++)
1303 if (d.
proj && d.
proj->project_vector(fwd.col(k),
true) ==
FAIL)
1309 if (d.
noise && whiten) {
1310 for (k = 0; k < 3; k++) {
1311 auto col_k = fwd.col(k);
1312 if (d.
noise->whiten_vector(col_k, col_k, nch) ==
FAIL)
constexpr auto FIFFV_MNE_SENSOR_COV
constexpr auto FIFFV_COORD_HEAD
constexpr auto FIFFV_COIL_CTF_REF_GOFF
constexpr auto FIFFV_COIL_CTF_REF_GRAD
constexpr auto FIFFV_COORD_UNKNOWN
constexpr auto FIFFV_COORD_MRI
constexpr auto FIFFV_MNE_NOISE_COV
constexpr auto FIFFV_COIL_CTF_REF_MAG
constexpr auto FIFFV_REF_MEG_CH
FIFF binary tag-stream layer: wraps a QIODevice to read and write FIFF tags, directories,...
Full FIFF measurement metadata: everything from FIFFB_MEAS / FIFFB_MEAS_INFO needed to interpret a re...
4x4 affine FIFF coordinate transform (FIFF_COORD_TRANS) annotated with source/destination coordinate-...
constexpr auto FIFFV_BEM_SURF_ID_BRAIN
Eigen::JacobiSVD< Eigen::Matrix3f > svd(S, Eigen::ComputeFullU|Eigen::ComputeFullV)
Software-gradiometer compensation wrapper that subtracts the reference-channel contribution from the ...
Boundary Element Method (BEM) volume-conductor model — layered triangulated surfaces,...
std::function aliases for the generic dipole field / potential / field-gradient callbacks driving the...
std::unique_ptr< InvDipoleForward > dipole_forward(InvDipoleFitData *d, float **rd, int ndip, std::unique_ptr< InvDipoleForward > old)
Compute the forward solution for one or more dipoles, applying projections and whitening.
Initial-guess grid for the dipole-fit optimiser, with per-guess forward fields pre-computed.
Dipole-fit workspace bundling sensor geometry, forward-model function pointers, noise covariance and ...
constexpr int COLUMN_NORM_NONE
constexpr int COLUMN_NORM_COMP
constexpr int COLUMN_NORM_LOC
Single equivalent current dipole (ECD) with position, moment and per-fit goodness/χ² metrics.
Best-fit sphere from a 3-D point cloud with closed-form and Nelder–Mead solvers.
Header-only Nelder–Mead simplex minimiser with pluggable cost and report callables.
One condition / averaging slice within a legacy MNELIB::MNEMeasData.
Legacy MNE-C measurement-data container assembling raw/evoked sets and their projection state.
Single SSP projection vector with kind/active flag and channel labels.
Lightweight triangulated surface (vertices, triangles, normals) used by surface-based routines.
Legacy MNE-C noise covariance container preserved for cross-toolchain compatibility.
constexpr auto MNE_COV_CH_EEG
constexpr auto MNE_COV_CH_MEG_GRAD
constexpr auto MNE_COV_CH_UNKNOWN
constexpr auto MNE_COV_CH_MEG_MAG
Core MNE data structures (source spaces, source estimates, hemispheres).
FIFF file I/O, in-memory data structures and high-level readers/writers.
Inverse source estimation (MNE, dSPM, sLORETA, dipole fitting).
Forward modelling — BEM solver, spherical models, sensor/coil definitions and the lead-field assembly...
constexpr int FWD_COIL_ACCURACY_NORMAL
constexpr int FWD_COIL_ACCURACY_ACCURATE
constexpr int FWD_BEM_UNKNOWN
static QString frame_name(int frame)
static FiffCoordTrans readMeasTransform(const QString &name)
static FiffCoordTrans readMriTransform(const QString &name)
Eigen::MatrixX3f apply_trans(const Eigen::MatrixX3f &rr, bool do_move=true) const
QSharedPointer< FiffDirNode > SPtr
Full FIFF measurement info: per-channel descriptors, sampling and filter setup, projectors,...
static bool readMegEegChannels(const QString &name, bool do_meg, bool do_eeg, const QStringList &bads, QList< FiffChInfo > &chsp, int &nmegp, int &neegp)
static bool readBadChannelsFromFile(const QString &name, QStringList &listOut)
FIFF tag-stream reader/writer: wraps a QIODevice and exposes typed read_* / write_* methods for every...
QSharedPointer< FiffStream > SPtr
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 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 QString fwd_bem_make_bem_sol_name(const QString &name)
Build a standard BEM solution file name from a model name.
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.
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 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_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 FwdBemModel::UPtr fwd_bem_load_homog_surface(const QString &name)
Load a single-layer (homogeneous) BEM model from a FIFF file.
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.
Container of FwdCoil instances acting both as the in-memory image of the coil_def....
static FwdCoilSet::UPtr read_coil_defs(const QString &name)
static FwdCoilSet::UPtr create_eeg_els(const QList< FIFFLIB::FiffChInfo > &chs, int nch, const FIFFLIB::FiffCoordTrans &t=FIFFLIB::FiffCoordTrans())
std::vector< FwdCoil::UPtr > coils
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_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)
Forward field computation function pointers and client data for MEG and EEG dipole fitting.
std::unique_ptr< FWDLIB::FwdCompData > meg_comp
fwdVecFieldFunc eeg_vec_pot
fwdVecFieldFunc meg_vec_field
Workspace for the dipole fitting objective function, holding forward model, measured field,...
std::unique_ptr< InvDipoleForward > fwd
Dipole fit workspace holding sensor geometry, forward model, noise covariance, and projection data.
std::unique_ptr< FWDLIB::FwdEegSphereModel > eeg_model
virtual ~InvDipoleFitData()
std::unique_ptr< FWDLIB::FwdCoilSet > meg_coils
static InvDipoleFitData * setup_dipole_fit_data(const QString &mriname, const QString &measname, const QString &bemname, Eigen::Vector3f *r0, FWDLIB::FwdEegSphereModel *eeg_model, int accurate_coils, const QString &badname, const QString &noisename, float grad_std, float mag_std, float eeg_std, float mag_reg, float grad_reg, float eeg_reg, int diagnoise, const QList< QString > &projnames, int include_meg, int include_eeg)
Master setup: read all inputs and build a ready-to-use fit workspace.
std::unique_ptr< FIFFLIB::FiffCoordTrans > mri_head_t
static int scale_dipole_fit_noise_cov(InvDipoleFitData *f, int nave)
Scale dipole-fit noise covariance for a given number of averages.
std::unique_ptr< MNELIB::MNEProjOp > proj
static bool print_fields(const Eigen::Vector3f &rd, const Eigen::Vector3f &Q, float time, float integ, InvDipoleFitData &fit, const MNELIB::MNEMeasData &data, QTextStream &out)
Write the measured and the predicted field of a dipole, channel by channel.
std::unique_ptr< MNELIB::MNECovMatrix > noise
std::unique_ptr< dipoleFitFuncsRec > bem_funcs
static int scale_noise_cov(InvDipoleFitData *f, int nave)
Scale the noise-covariance matrix for a given number of averages.
std::unique_ptr< dipoleFitFuncsRec > sphere_funcs
static std::unique_ptr< InvDipoleForward > dipole_forward_one(InvDipoleFitData *d, const Eigen::Vector3f &rd, std::unique_ptr< InvDipoleForward > old)
Compute the forward solution for a single dipole position.
static bool fit_one(InvDipoleFitData *fit, InvGuessData *guess, float time, Eigen::Ref< Eigen::VectorXf > B, int verbose, InvEcd &res)
Fit a single dipole to the given data.
static int compute_dipole_field(InvDipoleFitData &d, const Eigen::Vector3f &rd, int whiten, Eigen::Ref< Eigen::MatrixXf > fwd)
Compute the forward field for a dipole at the given location.
static int setup_forward_model(InvDipoleFitData *d, MNELIB::MNECTFCompDataSet *comp_data, FWDLIB::FwdCoilSet *comp_coils)
Set up the sphere-model and (optionally) BEM forward functions.
std::unique_ptr< FWDLIB::FwdBemModel > bem_model
static int select_dipole_fit_noise_cov(InvDipoleFitData *f, MNELIB::MNEMeasData *meas, int nave, const int *sels)
Select and weight the noise-covariance for the active channel set.
std::unique_ptr< FWDLIB::FwdCoilSet > eeg_els
std::unique_ptr< dipoleFitFuncsRec > mag_dipole_funcs
static std::unique_ptr< MNELIB::MNECovMatrix > ad_hoc_noise(FWDLIB::FwdCoilSet *meg, FWDLIB::FwdCoilSet *eeg, float grad_std, float mag_std, float eeg_std)
Create an ad-hoc diagonal noise-covariance matrix.
std::unique_ptr< MNELIB::MNECovMatrix > noise_orig
dipoleFitFuncsRec * funcs
Stores forward field matrices and SVD decomposition for magnetic dipole fitting.
Eigen::Matrix< float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > uu
Eigen::Matrix< float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > vv
Single equivalent current dipole with position, orientation, amplitude, and goodness-of-fit.
Precomputed guess point grid with forward fields for initial dipole position candidates.
std::vector< InvDipoleForward::UPtr > guess_fwd
Eigen::Matrix< float, Eigen::Dynamic, 3, Eigen::RowMajor > rr
static bool simplex_minimize(Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic > &p, Eigen::Matrix< T, Eigen::Dynamic, 1 > &y, T ftol, T stol, CostFunc &&func, int max_eval, int &neval, int report, ReportFunc &&report_func)
static bool fit_sphere_to_points(const Eigen::MatrixXf &rr, float simplex_size, Eigen::VectorXf &r0, float &R)
static std::unique_ptr< MNECovMatrix > read(const QString &name, int kind)
static std::unique_ptr< MNECovMatrix > create(int kind, int ncov, const QStringList &names, const Eigen::VectorXd &cov, const Eigen::VectorXd &cov_diag)
static int lt_packed_index(int j, int k)
Collection of CTF third-order gradient compensation operators.
static std::unique_ptr< MNECTFCompDataSet > read(const QString &name)
Measurement data container for MNE inverse and dipole-fit computations.
QList< FIFFLIB::FiffChInfo > chs
int getValuesAtTime(float time, float integ, int nch, bool use_abs, float *value) const
static bool makeProjection(const QList< QString > &projnames, const QList< FIFFLIB::FiffChInfo > &chs, int nch, std::unique_ptr< MNEProjOp > &result)
Load and combine SSP projection operators from files for the selected channels.
Lightweight triangulated surface (vertices, triangles, normals).