27#include <QCoreApplication>
56: m_pSettings(pSettings)
75void ComputeFwd::initFwd()
77 m_bInitialized =
false;
84 m_listMegChs = QList<FiffChInfo>();
85 m_listEegChs = QList<FiffChInfo>();
86 m_listCompChs = QList<FiffChInfo>();
104 std::unique_ptr<QTextStream> filteredStream;
110 qInfo(
"Source space : %s", m_pSettings->srcname.toUtf8().constData());
111 if (!(m_pSettings->transname.isEmpty()) || !(m_pSettings->mriname.isEmpty())) {
112 qInfo(
"MRI -> head transform source : %s", !(m_pSettings->mriname.isEmpty()) ? m_pSettings->mriname.toUtf8().constData() : m_pSettings->transname.toUtf8().constData());
114 qInfo(
"MRI and head coordinates are assumed to be identical.");
116 qInfo(
"Measurement data : %s", m_pSettings->measname.toUtf8().constData());
117 if (!m_pSettings->bemname.isEmpty()) {
118 qInfo(
"BEM model : %s", m_pSettings->bemname.toUtf8().constData());
120 qInfo(
"Sphere model : origin at (% 7.2f % 7.2f % 7.2f) mm",
121 1000.0f * m_pSettings->r0[
X], 1000.0f * m_pSettings->r0[
Y], 1000.0f * m_pSettings->r0[
Z]);
122 if (m_pSettings->include_eeg) {
123 if (m_pSettings->eeg_model_file.isEmpty()) {
124 qWarning(
"EEG model file not specified; using default.");
127 m_eegModels->fwd_list_eeg_sphere_models();
129 if (m_pSettings->eeg_model_name.isEmpty()) {
130 m_pSettings->eeg_model_name = QString(
"Default");
132 m_eegModel = m_eegModels->fwd_select_eeg_sphere_model(m_pSettings->eeg_model_name);
137 if (!m_eegModel->fwd_setup_eeg_sphere_model(m_pSettings->eeg_sphere_rad, m_pSettings->use_equiv_eeg, 3)) {
141 qInfo(
"Using EEG sphere model \"%s\" with scalp radius %7.1f mm",
142 m_pSettings->eeg_model_name.toUtf8().constData(), 1000 * m_pSettings->eeg_sphere_rad);
143 qInfo(
"%s the electrode locations to scalp", m_pSettings->scale_eeg_pos ?
"Scale" :
"Do not scale");
145 m_eegModel->scale_pos = m_pSettings->scale_eeg_pos;
146 m_eegModel->r0 = m_pSettings->r0;
149 qInfo(
"%s field computations", m_pSettings->accurate ?
"Accurate" :
"Standard");
150 qInfo(
"Do computations in %s coordinates.", FiffCoordTrans::frame_name(m_pSettings->coord_frame).toUtf8().constData());
151 qInfo(
"%s source orientations", m_pSettings->fixed_ori ?
"Fixed" :
"Free");
152 if (m_pSettings->compute_grad) {
153 qInfo(
"Compute derivatives with respect to source location coordinates");
155 qInfo(
"Destination for the solution : %s", m_pSettings->solname.toUtf8().constData());
156 if (m_pSettings->do_all) {
157 qInfo(
"Calculate solution for all source locations.");
159 if (m_pSettings->nlabel > 0) {
160 qInfo(
"Source space will be restricted to sources in %d labels", m_pSettings->nlabel);
164 qInfo(
"Reading %s...", m_pSettings->srcname.toUtf8().constData());
168 for (k = 0, m_iNSource = 0; k < static_cast<int>(m_spaces.size()); k++) {
169 if (m_pSettings->do_all) {
170 m_spaces[k]->enable_all_sources();
172 m_iNSource += m_spaces[k]->nuse;
174 if (m_iNSource == 0) {
175 qCritical(
"No sources are active in these source spaces. --all option should be used.");
178 qInfo(
"Read %d source spaces a total of %d active source locations",
static_cast<int>(m_spaces.size()), m_iNSource);
184 if (!m_pSettings->mriname.isEmpty()) {
186 if (m_mri_head_t.isEmpty()) {
190 QFile mriFile(m_pSettings->mriname);
192 if (mriStream->open()) {
193 m_mri_id.version = mriStream->id().version;
194 m_mri_id.machid[0] = mriStream->id().machid[0];
195 m_mri_id.machid[1] = mriStream->id().machid[1];
196 m_mri_id.time = mriStream->id().time;
203 if (m_mri_id.isEmpty()) {
204 qCritical(
"Couldn't read MRI file id (How come?)");
207 }
else if (!m_pSettings->transname.isEmpty()) {
216 m_mri_head_t.print();
220 if (!m_pSettings->pFiffInfo) {
221 QFile measname(m_pSettings->measname);
224 FIFFLIB::FiffInfo fiffInfo;
225 if (!pStream->open()) {
226 qCritical() <<
"Could not open Stream.";
230 if (!pStream->read_meas_info(pStream->dirtree(), fiffInfo, DirNode)) {
231 qCritical() <<
"Could not find the channel information.";
235 m_pInfoBase = QSharedPointer<FIFFLIB::FiffInfo>(
new FiffInfo(fiffInfo));
237 m_pInfoBase = m_pSettings->pFiffInfo;
240 qCritical(
"ComputeFwd::initFwd(): no FiffInfo");
243 m_pInfoBase->mne_read_meg_comp_eeg_ch_info(m_listMegChs,
251 if (!m_pSettings->meg_head_t.isEmpty()) {
252 m_meg_head_t = m_pSettings->meg_head_t;
254 if (m_meg_head_t.isEmpty()) {
255 qCritical(
"MEG -> head coordinate transformation not found.");
259 m_iNChan = iNMeg + iNEeg;
262 qInfo(
"Read %3d MEG channels from %s", iNMeg, m_pSettings->measname.toUtf8().constData());
265 qInfo(
"Read %3d MEG compensation channels from %s", iNComp, m_pSettings->measname.toUtf8().constData());
268 qInfo(
"Read %3d EEG channels from %s", iNEeg, m_pSettings->measname.toUtf8().constData());
270 if (!m_pSettings->include_meg) {
271 qInfo(
"MEG not requested. MEG channels omitted.");
272 m_listMegChs.clear();
273 m_listCompChs.clear();
277 m_meg_head_t.print();
278 if (!m_pSettings->include_eeg) {
279 qInfo(
"EEG not requested. EEG channels omitted.");
280 m_listEegChs.clear();
290 if (m_pSettings->include_meg) {
291 m_qPath = QString(QCoreApplication::applicationDirPath() +
"/../resources/general/coilDefinitions/coil_def.dat");
292 if (!QCoreApplication::startingUp()) {
293 m_qPath = QCoreApplication::applicationDirPath() + QString(
"/../resources/general/coilDefinitions/coil_def.dat");
294 }
else if (!QFile::exists(m_qPath)) {
295 m_qPath =
"../resources/general/coilDefinitions/coil_def.dat";
309 if (m_compData->ncomp > 0) {
310 qInfo(
"%d compensation data sets in %s", m_compData->ncomp, m_pSettings->measname.toUtf8().constData());
312 m_listCompChs.clear();
324 m_megcoils = m_templates->create_meg_coils(m_listMegChs,
332 m_compcoils = m_templates->create_meg_coils(m_listCompChs,
347 qInfo(
"MRI coordinate coil definitions created.");
349 m_megcoils = m_templates->create_meg_coils(m_listMegChs,
358 m_compcoils = m_templates->create_meg_coils(m_listCompChs,
370 qInfo(
"Head coordinate coil definitions created.");
379 qInfo(
"Source spaces are now in %s coordinates.", FiffCoordTrans::frame_name(m_pSettings->coord_frame).toUtf8().constData());
383 if (!m_pSettings->bemname.isEmpty()) {
385 m_pSettings->bemname = bemsolname;
387 qInfo(
"Setting up the BEM model using %s...", m_pSettings->bemname.toUtf8().constData());
388 qInfo(
"Loading surfaces...");
392 qInfo(
"Three-layer model surfaces loaded.");
398 qInfo(
"Homogeneous model surface loaded.");
400 if (iNEeg > 0 && m_bemModel->nsurf == 1) {
401 qCritical(
"Cannot use a homogeneous model in EEG calculations.");
404 qInfo(
"Loading the solution matrix...");
405 if (m_bemModel->fwd_bem_load_recompute_solution(m_pSettings->bemname.toUtf8().data(),
FWD_BEM_UNKNOWN,
false) ==
FAIL) {
409 qInfo(
"Employing the head->MRI coordinate transform with the BEM model.");
410 if (m_bemModel->fwd_bem_set_head_mri_t(m_mri_head_t) ==
FAIL) {
414 qInfo(
"BEM model %s is now set up", m_bemModel->sol_name.toUtf8().constData());
416 qInfo(
"Using the sphere model.");
417 m_bemModel = std::make_unique<FwdBemModel>();
422 if (m_pSettings->filter_spaces) {
423 if (!m_pSettings->mindistoutname.isEmpty()) {
424 filteredFile.setFileName(m_pSettings->mindistoutname);
425 if (!filteredFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
426 qCritical() << m_pSettings->mindistoutname;
429 filteredStream = std::make_unique<QTextStream>(&filteredFile);
430 qInfo(
"Omitted source space points will be output to : %s", m_pSettings->mindistoutname.toUtf8().constData());
433 m_pSettings->bemname,
436 filteredStream.get(), m_pSettings->use_threads);
438 m_bInitialized =
true;
443void ComputeFwd::populateMetadata(MNEForwardSolution& fwd)
450 int nmeg = m_megcoils ? m_megcoils->ncoil() : 0;
451 int neeg = m_eegels ? m_eegels->ncoil() : 0;
452 fwd.
nchan = nmeg + neeg;
464 for (
int i = 0; i < static_cast<int>(m_spaces.size()); ++i) {
466 fwd.
nsource += m_spaces[i]->nuse;
474 const int nOri = m_pSettings->fixed_ori ? 1 : 3;
477 for (
int i = 0, p = 0; i < static_cast<int>(m_spaces.size()); ++i) {
478 const MNESourceSpace& s = *m_spaces[i];
479 for (
int k = 0; k < s.
nuse; ++k, ++p) {
484 fwd.
source_nn.middleRows(3 * p, 3) = Matrix3f::Identity();
497 for (
int i = 0; i < nmeg; ++i) {
498 fwd.
info.
chs.append(m_listMegChs[i]);
501 for (
int i = 0; i < neeg; ++i) {
502 fwd.
info.
chs.append(m_listEegChs[i]);
507 if (!m_pSettings->measname.isEmpty()) {
508 QFile fileBad(m_pSettings->measname);
510 if (t_pStreamBads->open()) {
511 fwd.
info.
bads = t_pStreamBads->read_bad_channels(t_pStreamBads->dirtree());
512 t_pStreamBads->close();
521 if (!m_bInitialized) {
522 qCritical(
"ComputeFwd::calculateFwd - the forward computation could not be set up.");
525 auto fwdSolution = std::make_unique<MNEForwardSolution>();
526 populateMetadata(*fwdSolution);
531 iNMeg = m_megcoils->ncoil();
534 iNEeg = m_eegels->ncoil();
536 if (!m_bemModel || m_bemModel->nsurf == 0) {
537 m_pSettings->use_threads =
false;
541 if (m_spaces[0]->coord_frame != m_pSettings->coord_frame) {
549 if ((m_bemModel->compute_forward_meg(m_spaces,
553 m_pSettings->fixed_ori,
555 m_pSettings->use_threads,
556 *m_meg_forward.data(),
557 *m_meg_forward_grad.data(),
558 m_pSettings->compute_grad)) ==
FAIL) {
563 if ((m_bemModel->compute_forward_eeg(m_spaces,
565 m_pSettings->fixed_ori,
567 m_pSettings->use_threads,
568 *m_eeg_forward.data(),
569 *m_eeg_forward_grad.data(),
570 m_pSettings->compute_grad)) ==
FAIL) {
576 if (iNMeg > 0 && iNEeg > 0) {
577 if (m_meg_forward->data.cols() != m_eeg_forward->data.cols()) {
578 qWarning() <<
"The MEG and EEG forward solutions do not match";
581 fwdSolution->sol->clear();
582 fwdSolution->sol->nrow = m_meg_forward->nrow + m_eeg_forward->nrow;
583 fwdSolution->sol->ncol = m_meg_forward->ncol;
584 fwdSolution->sol->data = MatrixXd(fwdSolution->sol->nrow, fwdSolution->sol->ncol);
585 fwdSolution->sol->data.block(0, 0, m_meg_forward->nrow, m_meg_forward->ncol) = m_meg_forward->data;
586 fwdSolution->sol->data.block(m_meg_forward->nrow, 0, m_eeg_forward->nrow, m_eeg_forward->ncol) = m_eeg_forward->data;
587 fwdSolution->sol->row_names = m_meg_forward->row_names;
588 fwdSolution->sol->row_names.append(m_eeg_forward->row_names);
589 fwdSolution->sol->col_names = m_meg_forward->col_names;
590 }
else if (iNMeg > 0) {
591 fwdSolution->sol = m_meg_forward;
593 fwdSolution->sol = m_eeg_forward;
596 if (m_pSettings->compute_grad) {
597 if (iNMeg > 0 && iNEeg > 0) {
598 if (m_meg_forward_grad->data.cols() != m_eeg_forward_grad->data.cols()) {
599 qWarning() <<
"The MEG and EEG forward solutions do not match";
602 fwdSolution->sol_grad->clear();
603 fwdSolution->sol_grad->nrow = m_meg_forward_grad->nrow + m_eeg_forward_grad->nrow;
604 fwdSolution->sol_grad->ncol = m_meg_forward_grad->ncol;
605 fwdSolution->sol_grad->data = MatrixXd(fwdSolution->sol_grad->nrow, fwdSolution->sol_grad->ncol);
606 fwdSolution->sol_grad->data.block(0, 0, m_meg_forward_grad->nrow, m_meg_forward_grad->ncol) = m_meg_forward_grad->data;
607 fwdSolution->sol_grad->data.block(m_meg_forward_grad->nrow, 0, m_eeg_forward_grad->nrow, m_eeg_forward_grad->ncol) = m_eeg_forward_grad->data;
608 fwdSolution->sol_grad->row_names = m_meg_forward_grad->row_names;
609 fwdSolution->sol_grad->row_names.append(m_eeg_forward_grad->row_names);
610 fwdSolution->sol_grad->col_names = m_meg_forward_grad->col_names;
611 }
else if (iNMeg > 0) {
612 fwdSolution->sol_grad = m_meg_forward_grad;
614 fwdSolution->sol_grad = m_eeg_forward_grad;
629 iNMeg = m_megcoils->ncoil();
634 iNComp = m_compcoils->ncoil();
644 m_megcoils = m_templates->create_meg_coils(m_listMegChs,
652 m_compcoils = m_templates->create_meg_coils(m_listCompChs,
661 m_megcoils = m_templates->create_meg_coils(m_listMegChs,
670 m_compcoils = m_templates->create_meg_coils(m_listCompChs,
680 if (m_spaces[0]->coord_frame != m_pSettings->coord_frame) {
687 if ((m_bemModel->compute_forward_meg(m_spaces,
691 m_pSettings->fixed_ori,
693 m_pSettings->use_threads,
694 *m_meg_forward.data(),
695 *m_meg_forward_grad.data(),
696 m_pSettings->compute_grad)) ==
FAIL) {
701 m_meg_head_t = transDevHead;
705 fwd.
sol->data.block(0, 0, m_meg_forward->nrow, m_meg_forward->ncol) = m_meg_forward->data;
706 if (m_pSettings->compute_grad) {
707 fwd.
sol_grad->data.block(0, 0, m_meg_forward_grad->nrow, m_meg_forward_grad->ncol) = m_meg_forward_grad->data;
constexpr auto FIFFV_COORD_HEAD
constexpr auto FIFFV_COORD_DEVICE
constexpr auto FIFFV_COORD_MRI
constexpr auto FIFFV_MNE_FREE_ORI
constexpr auto FIFFV_MNE_FIXED_ORI
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...
return FiffCoordTrans(from_frame, to_frame, R, moveVec)
Top-level driver that assembles the MEG/EEG lead-field matrix G from a source space,...
Forward solution (gain matrix mapping source dipoles to sensor measurements).
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_COIL_ACCURACY_NORMAL
constexpr int FWD_COIL_ACCURACY_ACCURATE
constexpr int FWD_BEM_UNKNOWN
static bool checkEegLocations(const QList< FiffChInfo > &chs, int nch)
Labelled 4x4 FIFF affine: source frame, destination frame, rotation, translation and cached inverse.
static FiffCoordTrans combine(int from, int to, const FiffCoordTrans &t1, const FiffCoordTrans &t2)
FiffCoordTrans inverted() const
QSharedPointer< FiffDirNode > SPtr
128-bit FIFF identifier: hardware machine ID plus creation time, stamped on every file and block.
FiffCoordTrans dev_head_t
FIFF named matrix: dense / sparse Eigen matrix plus row-name and column-name string lists.
QSharedPointer< FiffStream > SPtr
std::unique_ptr< MNELIB::MNEForwardSolution > calculateFwd()
bool updateHeadPos(const FIFFLIB::FiffCoordTrans &transDevHead, MNELIB::MNEForwardSolution &fwd)
ComputeFwd(std::shared_ptr< ComputeFwdSettings > pSettings)
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 QString fwd_bem_make_bem_sol_name(const QString &name)
Build a standard BEM solution file name from a model name.
static FwdBemModel::UPtr fwd_bem_load_homog_surface(const QString &name)
Load a single-layer (homogeneous) BEM model from a FIFF file.
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())
static UPtr fwd_load_eeg_sphere_models(const QString &p_sFileName, UPtr now)
static std::unique_ptr< MNECTFCompDataSet > read(const QString &name)
In-memory representation of an -fwd.fif forward solution.
FIFFLIB::fiff_int_t nsource
FIFFLIB::FiffInfoBase info
MNELIB::MNESourceSpaces src
FIFFLIB::fiff_int_t source_ori
FIFFLIB::FiffCoordTrans mri_head_t
FIFFLIB::FiffNamedMatrix::SDPtr sol_grad
Eigen::MatrixX3f source_nn
Eigen::MatrixX3f source_rr
FIFFLIB::fiff_int_t coord_frame
FIFFLIB::fiff_int_t nchan
FIFFLIB::FiffNamedMatrix::SDPtr sol
static int restrict_sources_to_labels(std::vector< std::unique_ptr< MNESourceSpace > > &spaces, const QStringList &labels, int nlabel)
static int filter_source_spaces(const MNESurface &surf, float limit, const FIFFLIB::FiffCoordTrans &mri_head_t, std::vector< std::unique_ptr< MNESourceSpace > > &spaces, QTextStream *filtered)
static int read_source_spaces(const QString &name, std::vector< std::unique_ptr< MNESourceSpace > > &spaces)
static int transform_source_spaces_to(int coord_frame, const FIFFLIB::FiffCoordTrans &t, std::vector< std::unique_ptr< MNESourceSpace > > &spaces)
void append(const MNESourceSpace &space)
static FiffCoordTrans combine(int from, int to, const FiffCoordTrans &t1, const FiffCoordTrans &t2)
static FiffCoordTrans readFShead2mriTransform(const QString &name)
static FiffCoordTrans readMriTransform(const QString &name)
static FiffCoordTrans identity(int from, int to)