66#include <QCoreApplication>
79#ifndef FIFFV_COIL_CTF_GRAD
80#define FIFFV_COIL_CTF_GRAD 5001
83#ifndef FIFFV_COIL_CTF_REF_MAG
84#define FIFFV_COIL_CTF_REF_MAG 5002
87#ifndef FIFFV_COIL_CTF_REF_GRAD
88#define FIFFV_COIL_CTF_REF_GRAD 5003
91#ifndef FIFFV_COIL_CTF_OFFDIAG_REF_GRAD
92#define FIFFV_COIL_CTF_OFFDIAG_REF_GRAD 5004
112,
r0(Eigen::Vector3f::Zero())
132 int fit_sphere_to_bem =
true;
141 qInfo(
"\nSetting up the BEM model using %s...",d->
bemname.toUtf8().constData());
142 qInfo(
"\nLoading surfaces...");
145 qInfo(
"Three-layer model surfaces loaded.");
151 qInfo(
"Homogeneous model surface loaded.");
154 qCritical(
"Cannot use a homogeneous model in EEG calculations.");
157 qInfo(
"\nLoading the solution matrix...");
160 qInfo(
"Employing the head->MRI coordinate transform with the BEM model.");
164 qInfo(
"BEM model %s is now set up",d->
bem_model->sol_name.toUtf8().constData());
168 if (fit_sphere_to_bem) {
170 float simplex_size = 2e-2;
179 d->
r0 = r0_vec.head<3>();
182 qInfo(
"Fitted sphere model origin : %6.1f %6.1f %6.1f mm rad = %6.1f mm.",
183 1000*d->
r0[0],1000*d->
r0[1],1000*d->
r0[2],1000*R);
185 d->
bem_funcs = std::make_unique<dipoleFitFuncsRec>();
196 qInfo(
"Compensation setup done.");
198 qInfo(
"MEG solution matrix...");
211 qInfo(
"\tEEG solution matrix...");
221 qCritical(
"EEG sphere model not defined.");
224 d->
sphere_funcs = std::make_unique<dipoleFitFuncsRec>();
249 qInfo(
"Sphere model origin : %6.1f %6.1f %6.1f mm.",
250 1000*d->
r0[0],1000*d->
r0[1],1000*d->
r0[2]);
292 Eigen::VectorXd stds;
296 qInfo(
"Using standard noise values "
297 "(MEG grad : %6.1f fT/cm MEG mag : %6.1f fT EEG : %6.1f uV)\n",
298 1e13*grad_std,1e15*mag_std,1e6*eeg_std);
302 nchan = nchan + meg->
ncoil();
304 nchan = nchan + eeg->
ncoil();
310 for (k = 0; k < meg->
ncoil(); k++, n++) {
311 if (meg->
coils[k]->is_axial_coil()) {
312 stds[n] = mag_std*mag_std;
317 stds[n] = 1e6*stds[n];
321 stds[n] = grad_std*grad_std;
326 for (k = 0; k < eeg->
ncoil(); k++, n++) {
327 stds[n] = eeg_std*eeg_std;
339 float nave_ratio =
static_cast<float>(f->
nave) /
static_cast<float>(
nave);
345 if (f->
noise->cov.size() > 0) {
346 qInfo(
"Decomposing the sensor noise covariance matrix...");
350 for (k = 0; k < f->
noise->ncov*(f->
noise->ncov+1)/2; k++)
351 f->
noise->cov[k] = nave_ratio*f->
noise->cov[k];
352 for (k = 0; k < f->
noise->ncov; k++) {
353 f->
noise->lambda[k] = nave_ratio*f->
noise->lambda[k];
354 if (f->
noise->lambda[k] < 0.0)
355 f->
noise->lambda[k] = 0.0;
361 for (k = 0; k < f->
noise->ncov; k++)
362 f->
noise->cov_diag[k] = nave_ratio*f->
noise->cov_diag[k];
363 qInfo(
"Decomposition not needed for a diagonal noise covariance matrix.");
367 qInfo(
"Effective nave is now %d",
nave);
376 float nave_ratio =
static_cast<float>(f->
nave) /
static_cast<float>(
nave);
384 if (f->
noise->cov.size() > 0) {
388 qInfo(
"Decomposing the noise covariance...");
389 if (f->
noise->cov.size() > 0) {
392 for (k = 0; k < f->
noise->ncov; k++) {
393 if (f->
noise->lambda[k] < 0.0)
394 f->
noise->lambda[k] = 0.0;
397 for (k = 0; k < f->
noise->ncov*(f->
noise->ncov+1)/2; k++)
398 f->
noise->cov[k] = nave_ratio*f->
noise->cov[k];
399 for (k = 0; k < f->
noise->ncov; k++) {
400 f->
noise->lambda[k] = nave_ratio*f->
noise->lambda[k];
401 if (f->
noise->lambda[k] < 0.0)
402 f->
noise->lambda[k] = 0.0;
408 for (k = 0; k < f->
noise->ncov; k++)
409 f->
noise->cov_diag[k] = nave_ratio*f->
noise->cov_diag[k];
410 qInfo(
"Decomposition not needed for a diagonal noise covariance matrix.");
414 qInfo(
"Effective nave is now %d",
nave);
454 float *w = wVec.data();
455 int nomit_meg, nomit_eeg,
nmeg,
neeg;
458 nomit_meg = nomit_eeg = 0;
465 bool selected =
false;
466 for (
int c = 0; c < meas->
nchan; c++) {
468 meas->
chs[c].ch_name,
469 Qt::CaseInsensitive) == 0) {
470 selected = sels[c] != 0;
485 if (
nmeg > 0 &&
nmeg - nomit_meg > 0 &&
nmeg - nomit_meg < min_nchan) {
486 qCritical(
"Too few MEG channels remaining");
489 if (
neeg > 0 &&
neeg - nomit_eeg > 0 &&
neeg - nomit_eeg < min_nchan) {
490 qCritical(
"Too few EEG channels remaining");
494 if (nomit_meg + nomit_eeg > 0) {
495 if (f->
noise->cov.size() > 0) {
496 for (j = 0; j < f->
noise->ncov; j++)
497 for (k = 0; k <= j; k++) {
502 for (j = 0; j < f->
noise->ncov; j++) {
503 f->
noise->cov_diag[j] *= w[j] * w[j];
523 const QString &measname,
528 const QString &badname,
529 const QString &noisename,
537 const QList<QString> &projnames,
541 auto res = std::make_unique<InvDipoleFitData>();
545 QStringList file_bads;
548 std::unique_ptr<MNECovMatrix> cov;
549 std::unique_ptr<FwdCoilSet> templates;
550 std::unique_ptr<MNECTFCompDataSet> comp_data;
551 std::unique_ptr<FwdCoilSet> comp_coils;
556 if (!mriname.isEmpty()) {
558 if (res->mri_head_t->isEmpty())
562 qWarning(
"Source of MRI / head transform required for the BEM model is missing");
566 float move[] = { 0.0, 0.0, 0.0 };
567 float rot[3][3] = { { 1.0, 0.0, 0.0 },
570 Eigen::Matrix3f rotMat;
571 rotMat << rot[0][0], rot[0][1], rot[0][2],
572 rot[1][0], rot[1][1], rot[1][2],
573 rot[2][0], rot[2][1], rot[2][2];
574 Eigen::Vector3f moveVec = Eigen::Map<Eigen::Vector3f>(move);
578 res->mri_head_t->print();
580 if (res->meg_head_t->isEmpty())
582 res->meg_head_t->print();
586 if (!badname.isEmpty()) {
589 nbad = badlist.size();
590 qInfo(
"%d bad channels read from %s.", nbad, badname.toUtf8().data());
593 QFile measFile(measname);
595 if (measStream->open()) {
596 file_bads = measStream->read_bad_channels(measStream->dirtree());
597 file_nbad = file_bads.size();
602 if (badlist.isEmpty())
604 for (k = 0; k < file_nbad; k++) {
605 badlist.append(file_bads[k]);
609 qInfo(
"%d bad channels read from the data file.",file_nbad);
611 qInfo(
"%d bad channels total.",nbad);
625 qInfo(
"Will use %3d MEG channels from %s",res->nmeg,measname.toUtf8().data());
627 qInfo(
"Will use %3d EEG channels from %s",res->neeg,measname.toUtf8().data());
629 int nch_total = res->nmeg + res->neeg;
630 res->ch_names.clear();
631 for (
int i = 0; i < nch_total; i++)
632 res->ch_names.append(res->chs[i].ch_name);
648 QString qPath = QString(QCoreApplication::applicationDirPath() +
"/../resources/general/coilDefinitions/coil_def.dat");
650 if ( !QCoreApplication::startingUp() )
651 qPath = QCoreApplication::applicationDirPath() + QString(
"/../resources/general/coilDefinitions/coil_def.dat");
652 else if (!file.exists())
653 qPath =
"../resources/general/coilDefinitions/coil_def.dat";
655 QByteArray coilfileBytes = qPath.toUtf8();
656 const char *coilfile = coilfileBytes.constData();
666 Q_ASSERT(res->meg_head_t);
667 res->meg_coils = templates->create_meg_coils(res->chs,
677 qInfo(
"Head coordinate coil definitions created.");
697 if (comp_data->ncomp > 0) {
698 QList<FiffChInfo> comp_chs;
701 qInfo(
"%d compensation data sets in %s",comp_data->ncomp,measname.toUtf8().data());
703 QFile compFile(measname);
705 if (!compStream->open())
709 if (!compStream->read_meas_info(compStream->dirtree(), compInfo, compInfoNode)) {
714 for (
int k = 0; k < compInfo.
chs.size(); k++) {
716 comp_chs.append(compInfo.
chs[k]);
722 comp_coils = templates->create_meg_coils(comp_chs,
729 qInfo(
"%d compensation channels in %s",comp_coils->ncoil(),measname.toUtf8().data());
749 if (res->proj && res->proj->nitems > 0) {
750 qInfo(
"Final projection operator is:");
751 { QTextStream errStream(stderr); res->proj->report(errStream, QStringLiteral(
"\t")); }
753 if (res->proj->assign_channels(res->ch_names,res->nmeg+res->neeg) ==
FAIL)
755 if (res->proj->make_proj() ==
FAIL)
759 qInfo(
"No projection will be applied to the data.");
764 if (!noisename.isEmpty()) {
767 qInfo(
"Read a %s noise-covariance matrix from %s",
768 cov->cov_diag.size() > 0 ?
"diagonal" :
"full", noisename.toUtf8().data());
771 if ((cov =
ad_hoc_noise(res->meg_coils.get(),res->eeg_els.get(),grad_std,mag_std,eeg_std)) ==
nullptr)
774 res->noise = cov->pick_chs_omit(res->ch_names,
782 qInfo(
"Picked appropriate channels from the noise-covariance matrix.");
788 if (res->proj && res->proj->nitems > 0 && res->proj->nvec > 0) {
789 if (res->proj->apply_cov(res->noise.get()) ==
FAIL)
791 qInfo(
"Projection applied to the covariance matrix.");
798 res->noise->revert_to_diag();
799 qInfo(
"Using only the main diagonal of the noise-covariance matrix.");
805 if (res->noise->cov.size() > 0) {
806 Eigen::Vector3f regs;
815 if (res->noise->classify_channels(res->chs,
816 res->nmeg+res->neeg) ==
FAIL)
821 for (k = 0, do_it = 0; k < res->noise->ncov; k++) {
823 regs[res->noise->ch_class[k]] > 0.0)
830 res->noise->regularize(regs);
832 qInfo(
"No regularization applied to the noise-covariance matrix");
838 qInfo(
"Decomposing the noise covariance...");
839 if (res->noise->cov.size() > 0) {
840 if (res->noise->decompose_eigen() ==
FAIL)
842 qInfo(
"Eigenvalue decomposition done.");
843 for (k = 0; k < res->noise->ncov; k++) {
844 if (res->noise->lambda[k] < 0.0)
845 res->noise->lambda[k] = 0.0;
849 qInfo(
"Decomposition not needed for a diagonal covariance matrix.");
850 if (res->noise->add_inv() ==
FAIL)
855 return res.release();
863 const Eigen::Vector3f& Q,
870 Eigen::VectorXf oneVec(data->
nchan);
875 qWarning(
"Cannot pick time: %7.1f ms",1000*time);
878 for (k = 0; k < data->
nchan; k++)
881 qInfo(
"%g ",1e15*oneVec[k]);
885 Eigen::MatrixXf fwd = Eigen::MatrixXf::Zero(nch, 3);
889 for (k = 0; k < data->
nchan; k++)
892 qInfo(
"%g ",1e15*(Q[0]*fwd(k,0)+Q[1]*fwd(k,1)+Q[2]*fwd(k,2)));
919 delete old; old =
nullptr;
923 res->
fwd.resize(m, nch);
924 res->
uu.resize(m, nch);
925 res->
vv.resize(m, m);
928 res->
rd.resize(ndip, 3);
933 for (k = 0; k < ndip; k++) {
934 res->
rd.row(k) = Eigen::Map<const Eigen::Vector3f>(rd[k]).transpose();
938 Eigen::MatrixXf this_fwd(d->
nmeg + d->
neeg, 3);
939 Eigen::Map<const Eigen::Vector3f> rd_k(rd[k]);
945 for (
int p = 0; p < 3; p++)
946 res->
fwd.row(3*k+p) = this_fwd.col(p).transpose();
952 for (p = 0; p < 3; p++)
953 S[p] = res->
fwd.row(3*k+p).squaredNorm();
955 for (p = 0; p < 3; p++)
956 res->
scales[3*k+p] = sqrt(S[p]);
962 res->
scales[3*k+0] = res->
scales[3*k+1] = res->
scales[3*k+2] = sqrt(S[0]+S[1]+S[2])/3.0;
964 for (p = 0; p < 3; p++) {
965 if (res->
scales[3*k+p] > 0.0) {
967 res->
fwd.row(3*k+p) *= res->
scales[3*k+p];
988 int udim = std::min(m,n);
989 JacobiSVD<MatrixXf> svd(res->
fwd, ComputeFullU | ComputeFullV);
990 res->
sing = svd.singularValues();
991 res->
uu = svd.matrixV().transpose().topRows(udim);
992 res->
vv = svd.matrixU().transpose().topRows(udim);
1004 const Eigen::Vector3f& rd,
1008 rds[0] =
const_cast<float*
>(rd.data());
1017static float fit_eval(
const VectorXf& rd,
const void *user)
1028 qInfo(
"ncomp = %d",ncomp);
1030 Eigen::Map<const VectorXf> Bmap(fuser->
B, fwd->
nch);
1031 for (c = 0, Bm2 = 0.0; c < ncomp; c++) {
1032 one = fwd->
uu.row(c).dot(Bmap);
1033 Bm2 = Bm2 + one*one;
1035 return fuser->
B2-Bm2;
1041static int find_best_guess(
const Eigen::Ref<const Eigen::VectorXf>& B,
1049 double B2,Bm2,this_good,one;
1055 B2 = B.squaredNorm();
1056 for (k = 0; k < guess->
nguess; k++) {
1058 if (fwd->
nch == nch) {
1059 ncomp = fwd->
sing[2]/fwd->
sing[0] > limit ? 3 : 2;
1060 for (c = 0, Bm2 = 0.0; c < ncomp; c++) {
1061 one = fwd->
uu.row(c).dot(B);
1062 Bm2 = Bm2 + one*one;
1064 this_good = 1.0 - (B2 - Bm2)/B2;
1065 if (this_good > good) {
1072 qWarning(
"No reasonable initial guess found.");
1083static MatrixXf make_initial_dipole_simplex(
const Eigen::Vector3f& r0,
1092 float x = sqrt(3.0f)/3.0f;
1093 float r = sqrt(6.0f)/12.0f;
1096 float rr[][3] = { { x , 0.0f, -r },
1099 { 0.0f, 0.0f, R } };
1101 MatrixXf simplex = MatrixXf::Zero(4, 3);
1103 for (
int j = 0; j < 4; j++) {
1104 simplex.row(j) = Eigen::Map<const Vector3f>(rr[j]).transpose() * size + r0.transpose();
1109static bool dipole_report_func(
int loop,
1110 const VectorXf& fitpar,
1115 qInfo(
"loop %d rd %7.2f %7.2f %7.2f fval %g %g par diff %g",
1116 loop,1000*fitpar[0],1000*fitpar[1],1000*fitpar[2],fval_lo,fval_hi,1000*par_diff);
1125 const Eigen::Ref<const Eigen::VectorXf>& B,
1126 const Eigen::Vector3f& rd,
1139 ncomp = fwd->
sing[2]/fwd->
sing[0] > limit ? 3 : 2;
1142 for (c = 0, Bm2 = 0.0; c < ncomp; c++) {
1143 one = fwd->
uu.row(c).dot(B);
1144 Q += (one/fwd->
sing[c]) * fwd->
vv.row(c).head(3).transpose();
1145 Bm2 = Bm2 + one*one;
1150 for (c = 0; c < 3; c++)
1151 Q[c] = fwd->
scales[c]*Q[c];
1152 res = B.squaredNorm() - Bm2;
1175 Eigen::Ref<Eigen::VectorXf> B,
1183 float ftol[] = { 1e-2f, 1e-2f };
1184 float atol[] = { 0.2e-3f, 0.2e-3f };
1187 int max_eval = 1000;
1188 int report_interval = verbose ? 1 : -1;
1191 float good,final_val;
1192 Eigen::Vector3f rd_final, Q;
1194 int k,neval,neval_tot,nchan,ncomp;
1201 if (fit->
proj && fit->
proj->project_vector(B,
true) ==
FAIL)
1204 if (fit->
noise->whiten_vector(B,B,nchan) ==
FAIL)
1209 if (find_best_guess(B,nchan,guess,limit,best,good) < 0)
1214 user.B2 = B.squaredNorm();
1216 user.report_dim =
false;
1219 rd_guess = guess->
rr.row(best).transpose();
1220 rd_final = rd_guess;
1224 for (k = 0; k < ntol; k++) {
1233 MatrixXf simplexMat = make_initial_dipole_simplex(rd_guess,size);
1234 for (
int p = 0; p < 4; p++)
1235 vals[p] = fit_eval(simplexMat.row(p),fit);
1238 auto cost = [fit](
const VectorXf& x) ->
float {
return fit_eval(x, fit); };
1249 dipole_report_func)) {
1255 float rv = 2.0f*(vals.maxCoeff()-vals.minCoeff())/(vals.maxCoeff()+vals.minCoeff());
1256 qWarning(
"Warning (t = %8.1f ms) : g = %6.1f %% final val = %7.3f rtol = %f",
1257 1000*time,100*(1 - vals[0]/
user.B2),vals[0],rv);
1261 rd_final = simplexMat.row(0).transpose();
1262 rd_guess = simplexMat.row(0).transpose();
1265 final_val = vals[0];
1273 if (fit_Q(fit,B,rd_final,
user.limit,Q,ncomp,final_val) ==
OK) {
1278 res.
good = 1.0 - final_val/
user.B2;
1281 res.
khi2 = final_val;
1283 res.
nfree = nchan-3-ncomp-fit->
proj->nvec;
1285 res.
nfree = nchan-3-ncomp;
1286 res.
neval = neval_tot;
1306 static const Eigen::Vector3f Qx(1.0f, 0.0f, 0.0f);
1307 static const Eigen::Vector3f Qy(0.0f, 1.0f, 0.0f);
1308 static const Eigen::Vector3f Qz(0.0f, 0.0f, 1.0f);
1321 Eigen::MatrixXf vec_meg(3,
nmeg);
1324 fwd.topRows(
nmeg) = vec_meg.transpose();
1326 auto fwd0 = fwd.col(0).head(
nmeg);
1327 auto fwd1 = fwd.col(1).head(
nmeg);
1328 auto fwd2 = fwd.col(2).head(
nmeg);
1345 Eigen::MatrixXf vec_eeg(3,
neeg);
1348 fwd.block(d.
nmeg, 0,
neeg, 3) = vec_eeg.transpose();
1350 auto fwd0 = fwd.col(0).segment(d.
nmeg,
neeg);
1351 auto fwd1 = fwd.col(1).segment(d.
nmeg,
neeg);
1352 auto fwd2 = fwd.col(2).segment(d.
nmeg,
neeg);
1365 for (k = 0; k < 3; k++)
1366 if (d.
proj && d.
proj->project_vector(fwd.col(k),
true) ==
FAIL)
1372 if (d.
noise && whiten) {
1373 for (k = 0; k < 3; k++) {
1374 auto col_k = fwd.col(k);
1375 if (d.
noise->whiten_vector(col_k,col_k,nch) ==
FAIL)
Sphere class declaration.
SimplexAlgorithm Template Implementation.
#define FIFFV_COIL_CTF_OFFDIAG_REF_GRAD
InvDipoleForward * dipole_forward(InvDipoleFitData *d, float **rd, int ndip, InvDipoleForward *old)
Compute the forward solution for one or more dipoles, applying projections and whitening.
void print_fields(const Eigen::Vector3f &rd, const Eigen::Vector3f &Q, float time, float integ, InvDipoleFitData *fit, MNEMeasData *data)
Electric Current Dipole (InvEcd) class declaration.
Dipole Fit Data class declaration.
constexpr int COLUMN_NORM_NONE
constexpr int COLUMN_NORM_COMP
constexpr int COLUMN_NORM_LOC
InvGuessData class declaration.
FwdBemModel class declaration.
FwdCompData class declaration.
Forward library type definitions.
FiffInfo class declaration.
FiffCoordTrans class declaration.
#define FIFFV_BEM_SURF_ID_BRAIN
FiffStream class declaration.
#define FIFFV_MNE_SENSOR_COV
#define FIFFV_COIL_CTF_REF_GRAD
#define FIFFV_MNE_NOISE_COV
#define FIFFV_COIL_CTF_REF_MAG
#define FIFFV_COORD_UNKNOWN
MNECovMatrix class declaration.
#define MNE_COV_CH_MEG_MAG
#define MNE_COV_CH_MEG_GRAD
#define MNE_COV_CH_UNKNOWN
MNEProjItem class declaration.
MNEMeasData class declaration.
MNEMeasDataSet class declaration.
MNESurface class declaration.
Core MNE data structures (source spaces, source estimates, hemispheres).
FIFF file I/O and data structures (raw, epochs, evoked, covariance, forward).
Inverse source estimation (MNE, dSPM, sLORETA, dipole fitting).
Forward modelling (BEM, MEG/EEG lead fields).
constexpr int FWD_COIL_ACCURACY_NORMAL
constexpr int FWD_COIL_ACCURACY_ACCURATE
constexpr int FWD_BEM_UNKNOWN
QSharedPointer< FiffDirNode > SPtr
FIFF measurement file information.
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)
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.
Collection of FwdCoil objects representing a full MEG or EEG sensor array.
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
This structure is used in the compensated field calculations.
static int fwd_comp_field_vec(const Eigen::Vector3f &rd, FwdCoilSet &coils, Eigen::Ref< Eigen::MatrixXf > res, void *client)
static int fwd_comp_field(const Eigen::Vector3f &rd, const Eigen::Vector3f &Q, FwdCoilSet &coils, Eigen::Ref< Eigen::VectorXf > res, void *client)
static FwdCompData * fwd_make_comp_data(MNELIB::MNECTFCompDataSet *set, FwdCoilSet *coils, FwdCoilSet *comp_coils, fwdFieldFunc field, fwdVecFieldFunc vec_field, fwdFieldGradFunc field_grad, void *client)
Multi-layer spherical head model for EEG forward computation.
static int fwd_eeg_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.
fwdVecFieldFunc eeg_vec_pot
fwdVecFieldFunc meg_vec_field
MNELIB::mneUserFreeFunc meg_client_free
Workspace for the dipole fitting objective function, holding forward model, measured field,...
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 InvDipoleForward * dipole_forward_one(InvDipoleFitData *d, const Eigen::Vector3f &rd, InvDipoleForward *old)
Compute the forward solution for a single dipole position.
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 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
Eigen::Matrix< float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > fwd
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.
static FiffCoordTrans readMriTransform(const QString &name)
static FiffCoordTrans readMeasTransform(const QString &name)
Eigen::MatrixX3f apply_trans(const Eigen::MatrixX3f &rr, bool do_move=true) const
static QString frame_name(int frame)