v2.0.0
Loading...
Searching...
No Matches
compute_fwd.cpp
Go to the documentation of this file.
1//=============================================================================================================
12
13//=============================================================================================================
14// INCLUDES
15//=============================================================================================================
16
17#include "compute_fwd.h"
19
20#include <fiff/fiff_stream.h>
21#include <fiff/fiff_info.h>
22
23//=============================================================================================================
24// QT INCLUDES
25//=============================================================================================================
26
27#include <QCoreApplication>
28#include <QFile>
29#include <QTextStream>
30
31//=============================================================================================================
32// USED NAMESPACES
33//=============================================================================================================
34
35using namespace FWDLIB;
36using namespace MNELIB;
37using namespace FIFFLIB;
38using namespace Eigen;
39
40//=============================================================================================================
41// CONSTANTS
42//=============================================================================================================
43
44constexpr int FAIL = -1;
45constexpr int OK = 0;
46
47constexpr int X = 0;
48constexpr int Y = 1;
49constexpr int Z = 2;
50
51//=============================================================================================================
52// DEFINE MEMBER METHODS
53//=============================================================================================================
54
55ComputeFwd::ComputeFwd(std::shared_ptr<ComputeFwdSettings> pSettings)
56 : m_meg_forward(new FiffNamedMatrix)
57 , m_meg_forward_grad(new FiffNamedMatrix)
58 , m_eeg_forward(new FiffNamedMatrix)
59 , m_eeg_forward_grad(new FiffNamedMatrix)
60 , m_pSettings(pSettings)
61{
62 initFwd();
63}
64
65//=============================================================================================================
66
70
71//=============================================================================================================
72
73//=============================================================================================================
74
75void ComputeFwd::initFwd()
76{
77 m_spaces.clear();
78 m_iNSource = 0;
79
80 m_mri_head_t = FiffCoordTrans();
81 m_meg_head_t = FiffCoordTrans();
82
83 m_listMegChs = QList<FiffChInfo>();
84 m_listEegChs = QList<FiffChInfo>();
85 m_listCompChs = QList<FiffChInfo>();
86
87 int iNMeg = 0;
88 int iNEeg = 0;
89 int iNComp = 0;
90
91 m_templates.reset();
92 m_megcoils.reset();
93 m_compcoils.reset();
94 m_eegels.reset();
95 m_eegModels.reset();
96 m_iNChan = 0;
97
98 int k;
99 m_mri_id = FiffId();
100 m_meas_id.clear();
101
102 QFile filteredFile;
103 QTextStream *filteredStream = nullptr;
104
105 m_eegModel.reset();
106 m_bemModel.reset();
107
108 // Report the setup
109 qInfo("Source space : %s",m_pSettings->srcname.toUtf8().constData());
110 if (!(m_pSettings->transname.isEmpty()) || !(m_pSettings->mriname.isEmpty())) {
111 qInfo("MRI -> head transform source : %s",!(m_pSettings->mriname.isEmpty()) ? m_pSettings->mriname.toUtf8().constData() : m_pSettings->transname.toUtf8().constData());
112 } else {
113 qInfo("MRI and head coordinates are assumed to be identical.");
114 }
115 qInfo("Measurement data : %s",m_pSettings->measname.toUtf8().constData());
116 if (!m_pSettings->bemname.isEmpty()) {
117 qInfo("BEM model : %s",m_pSettings->bemname.toUtf8().constData());
118 } else {
119 qInfo("Sphere model : origin at (% 7.2f % 7.2f % 7.2f) mm",
120 1000.0f*m_pSettings->r0[X],1000.0f*m_pSettings->r0[Y],1000.0f*m_pSettings->r0[Z]);
121 if (m_pSettings->include_eeg) {
122
123 if (m_pSettings->eeg_model_file.isEmpty()) {
124 qWarning("EEG model file not specified; using default.");
125 }
126 m_eegModels.reset(FwdEegSphereModelSet::fwd_load_eeg_sphere_models(m_pSettings->eeg_model_file,m_eegModels.release()));
127 m_eegModels->fwd_list_eeg_sphere_models();
128
129 if (m_pSettings->eeg_model_name.isEmpty()) {
130 m_pSettings->eeg_model_name = QString("Default");
131 }
132 m_eegModel.reset(m_eegModels->fwd_select_eeg_sphere_model(m_pSettings->eeg_model_name));
133 if (!m_eegModel) {
134 return;
135 }
136
137 if (!m_eegModel->fwd_setup_eeg_sphere_model(m_pSettings->eeg_sphere_rad,m_pSettings->use_equiv_eeg,3)) {
138 return;
139 }
140
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");
144
145 m_eegModel->scale_pos = m_pSettings->scale_eeg_pos;
146 m_eegModel->r0 = m_pSettings->r0;
147 }
148 }
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");
154 }
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.");
158 }
159 if (m_pSettings->nlabel > 0) {
160 qInfo("Source space will be restricted to sources in %d labels",m_pSettings->nlabel);
161 }
162
163 // Read the source locations
164 qInfo("Reading %s...",m_pSettings->srcname.toUtf8().constData());
165 if (MNESourceSpace::read_source_spaces(m_pSettings->srcname,m_spaces) != OK) {
166 return;
167 }
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();
171 }
172 m_iNSource += m_spaces[k]->nuse;
173 }
174 if (m_iNSource == 0) {
175 qCritical("No sources are active in these source spaces. --all option should be used.");
176 return;
177 }
178 qInfo("Read %d source spaces a total of %d active source locations", static_cast<int>(m_spaces.size()),m_iNSource);
179 if (MNESourceSpace::restrict_sources_to_labels(m_spaces,m_pSettings->labels,m_pSettings->nlabel) == FAIL) {
180 return;
181 }
182
183 // Read the MRI -> head coordinate transformation
184 if (!m_pSettings->mriname.isEmpty()) {
185 m_mri_head_t = FiffCoordTrans::readMriTransform(m_pSettings->mriname);
186 if (m_mri_head_t.isEmpty()) {
187 return;
188 }
189 {
190 QFile mriFile(m_pSettings->mriname);
191 FiffStream::SPtr mriStream(new FiffStream(&mriFile));
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;
197 mriStream->close();
198 } else {
199 mriStream->close();
200 m_mri_id = FiffId();
201 }
202 }
203 if (m_mri_id.isEmpty()) {
204 qCritical("Couldn't read MRI file id (How come?)");
205 return;
206 }
207 }
208 else if (!m_pSettings->transname.isEmpty()) {
209 FiffCoordTrans t = FiffCoordTrans::readFShead2mriTransform(m_pSettings->transname.toUtf8().data());
210 if (t.isEmpty()) {
211 return;
212 }
213 m_mri_head_t = t.inverted();
214 } else {
216 }
217 m_mri_head_t.print();
218
219 // Read the channel information and the MEG device -> head coordinate transformation
220
221 if(!m_pSettings->pFiffInfo) {
222
223 QFile measname(m_pSettings->measname);
225 FiffStream::SPtr pStream(new FiffStream(&measname));
226 FIFFLIB::FiffInfo fiffInfo;
227 if(!pStream->open()) {
228 qCritical() << "Could not open Stream.";
229 return;
230 }
231
232 if(!pStream->read_meas_info(pStream->dirtree(), fiffInfo, DirNode)){
233 qCritical() << "Could not find the channel information.";
234 return;
235 }
236 pStream->close();
237 m_pInfoBase = QSharedPointer<FIFFLIB::FiffInfo>(new FiffInfo(fiffInfo));
238 } else {
239 m_pInfoBase = m_pSettings->pFiffInfo;
240 }
241 if(!m_pInfoBase) {
242 qCritical ("ComputeFwd::initFwd(): no FiffInfo");
243 return;
244 }
245 m_pInfoBase->mne_read_meg_comp_eeg_ch_info(m_listMegChs,
246 iNMeg,
247 m_listCompChs,
248 iNComp,
249 m_listEegChs,
250 iNEeg,
251 m_meg_head_t,
252 m_meas_id);
253 if (!m_pSettings->meg_head_t.isEmpty()) {
254 m_meg_head_t = m_pSettings->meg_head_t;
255 }
256 if (m_meg_head_t.isEmpty()) {
257 qCritical("MEG -> head coordinate transformation not found.");
258 return;
259 }
260
261 m_iNChan = iNMeg + iNEeg;
262
263 if (iNMeg > 0) {
264 qInfo("Read %3d MEG channels from %s",iNMeg,m_pSettings->measname.toUtf8().constData());
265 }
266 if (iNComp > 0) {
267 qInfo("Read %3d MEG compensation channels from %s",iNComp,m_pSettings->measname.toUtf8().constData());
268 }
269 if (iNEeg > 0) {
270 qInfo("Read %3d EEG channels from %s",iNEeg,m_pSettings->measname.toUtf8().constData());
271 }
272 if (!m_pSettings->include_meg) {
273 qInfo("MEG not requested. MEG channels omitted.");
274 m_listMegChs.clear();
275 m_listCompChs.clear();
276 iNMeg = 0;
277 iNComp = 0;
278 }
279 else
280 m_meg_head_t.print();
281 if (!m_pSettings->include_eeg) {
282 qInfo("EEG not requested. EEG channels omitted.");
283 m_listEegChs.clear();
284 iNEeg = 0;
285 } else {
286 if (!FiffChInfo::checkEegLocations(m_listEegChs, iNEeg)) {
287 return;
288 }
289 }
290
291 // Create coil descriptions with transformation to head or MRI frame
292
293 if (m_pSettings->include_meg) {
294 m_qPath = QString(QCoreApplication::applicationDirPath() + "/../resources/general/coilDefinitions/coil_def.dat");
295 if ( !QCoreApplication::startingUp() ) {
296 m_qPath = QCoreApplication::applicationDirPath() + QString("/../resources/general/coilDefinitions/coil_def.dat");
297 } else if (!QFile::exists(m_qPath)) {
298 m_qPath = "../resources/general/coilDefinitions/coil_def.dat";
299 }
300
301 m_templates = FwdCoilSet::read_coil_defs(m_qPath);
302 if (!m_templates) {
303 return;
304 }
305
306 // Compensation data
307
308 m_compData = MNECTFCompDataSet::read(m_pSettings->measname);
309 if (!m_compData) {
310 return;
311 }
312 if (m_compData->ncomp > 0) {
313 qInfo("%d compensation data sets in %s",m_compData->ncomp,m_pSettings->measname.toUtf8().constData());
314 } else {
315 m_listCompChs.clear();
316 iNComp = 0;
317
318 m_compData.reset();
319 }
320 }
321 if (m_pSettings->coord_frame == FIFFV_COORD_MRI) {
322 FiffCoordTrans head_mri_t = m_mri_head_t.inverted();
324 if (meg_mri_t.isEmpty()) {
325 return;
326 }
327 m_megcoils = m_templates->create_meg_coils(m_listMegChs,
328 iNMeg,
330 meg_mri_t);
331 if (!m_megcoils) {
332 return;
333 }
334 if (iNComp > 0) {
335 m_compcoils = m_templates->create_meg_coils(m_listCompChs,
336 iNComp,
338 meg_mri_t);
339 if (!m_compcoils) {
340 return;
341 }
342 }
343 m_eegels = FwdCoilSet::create_eeg_els(m_listEegChs,
344 iNEeg,
345 head_mri_t);
346 if (!m_eegels) {
347 return;
348 }
349
350 qInfo("MRI coordinate coil definitions created.");
351 } else {
352 m_megcoils = m_templates->create_meg_coils(m_listMegChs,
353 iNMeg,
355 m_meg_head_t);
356 if (!m_megcoils) {
357 return;
358 }
359
360 if (iNComp > 0) {
361 m_compcoils = m_templates->create_meg_coils(m_listCompChs,
362 iNComp,
363 FWD_COIL_ACCURACY_NORMAL,m_meg_head_t);
364 if (!m_compcoils) {
365 return;
366 }
367 }
368 m_eegels = FwdCoilSet::create_eeg_els(m_listEegChs,
369 iNEeg);
370 if (!m_eegels) {
371 return;
372 }
373 qInfo("Head coordinate coil definitions created.");
374 }
375
376 // Transform the source spaces into the appropriate coordinates
377 {
378 if (MNESourceSpace::transform_source_spaces_to(m_pSettings->coord_frame,m_mri_head_t,m_spaces) != OK) {
379 return;
380 }
381 }
382 qInfo("Source spaces are now in %s coordinates.",FiffCoordTrans::frame_name(m_pSettings->coord_frame).toUtf8().constData());
383
384 // Prepare the BEM model if necessary
385
386 if (!m_pSettings->bemname.isEmpty()) {
387 QString bemsolname = FwdBemModel::fwd_bem_make_bem_sol_name(m_pSettings->bemname);
388 m_pSettings->bemname = bemsolname;
389
390 qInfo("Setting up the BEM model using %s...",m_pSettings->bemname.toUtf8().constData());
391 qInfo("Loading surfaces...");
392 m_bemModel = FwdBemModel::fwd_bem_load_three_layer_surfaces(m_pSettings->bemname);
393
394 if (m_bemModel) {
395 qInfo("Three-layer model surfaces loaded.");
396 }
397 else {
398 m_bemModel = FwdBemModel::fwd_bem_load_homog_surface(m_pSettings->bemname);
399 if (!m_bemModel) {
400 return;
401 }
402 qInfo("Homogeneous model surface loaded.");
403 }
404 if (iNEeg > 0 && m_bemModel->nsurf == 1) {
405 qCritical("Cannot use a homogeneous model in EEG calculations.");
406 return;
407 }
408 qInfo("Loading the solution matrix...");
409 if (m_bemModel->fwd_bem_load_recompute_solution(m_pSettings->bemname.toUtf8().data(),FWD_BEM_UNKNOWN,false) == FAIL) {
410 return;
411 }
412 if (m_pSettings->coord_frame == FIFFV_COORD_HEAD) {
413 qInfo("Employing the head->MRI coordinate transform with the BEM model.");
414 if (m_bemModel->fwd_bem_set_head_mri_t(m_mri_head_t) == FAIL) {
415 return;
416 }
417 }
418 qInfo("BEM model %s is now set up",m_bemModel->sol_name.toUtf8().constData());
419 } else {
420 qInfo("Using the sphere model.");
421 }
422
423 // Try to circumvent numerical problems by excluding points too close or outside the inner skull surface
424
425 if (m_pSettings->filter_spaces) {
426 if (!m_pSettings->mindistoutname.isEmpty()) {
427 filteredFile.setFileName(m_pSettings->mindistoutname);
428 if (!filteredFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
429 qCritical() << m_pSettings->mindistoutname;
430 return;
431 }
432 filteredStream = new QTextStream(&filteredFile);
433 qInfo("Omitted source space points will be output to : %s",m_pSettings->mindistoutname.toUtf8().constData());
434 }
435 MNESourceSpace::filter_source_spaces(m_pSettings->mindist,
436 m_pSettings->bemname,
437 m_mri_head_t,
438 m_spaces,
439 filteredStream,m_pSettings->use_threads);
440 delete filteredStream;
441 filteredStream = nullptr;
442 }
443}
444
445//=============================================================================================================
446
447void ComputeFwd::populateMetadata(MNEForwardSolution& fwd)
448{
449 fwd.coord_frame = m_pSettings->coord_frame;
450 fwd.source_ori = m_pSettings->fixed_ori ? FIFFV_MNE_FIXED_ORI : FIFFV_MNE_FREE_ORI;
451 fwd.surf_ori = false;
452 fwd.mri_filename = m_pSettings->mriname;
453
454 int nmeg = m_megcoils ? m_megcoils->ncoil() : 0;
455 int neeg = m_eegels ? m_eegels->ncoil() : 0;
456 fwd.nchan = nmeg + neeg;
457
458 fwd.mri_head_t = m_mri_head_t;
459 fwd.mri_id = m_mri_id;
460
461 // Source spaces: store in MRI frame (FIFF convention), keep m_spaces in computation frame
462 {
463 if (MNESourceSpace::transform_source_spaces_to(FIFFV_COORD_MRI, m_mri_head_t, m_spaces) != OK) {
464 return;
465 }
466 fwd.src.clear();
467 fwd.nsource = 0;
468 for (int i = 0; i < static_cast<int>(m_spaces.size()); ++i) {
469 fwd.src.append(*m_spaces[i]);
470 fwd.nsource += m_spaces[i]->nuse;
471 }
472 if (MNESourceSpace::transform_source_spaces_to(m_pSettings->coord_frame, m_mri_head_t, m_spaces) != OK) {
473 return;
474 }
475 }
476
477 // Measurement provenance
478 fwd.info.filename = m_pSettings->measname;
479 fwd.info.meas_id = m_meas_id;
480 fwd.info.dev_head_t = m_meg_head_t;
481 fwd.info.nchan = fwd.nchan;
482
483 fwd.info.chs.clear();
484 fwd.info.ch_names.clear();
485 for (int i = 0; i < nmeg; ++i) {
486 fwd.info.chs.append(m_listMegChs[i]);
487 fwd.info.ch_names.append(m_listMegChs[i].ch_name);
488 }
489 for (int i = 0; i < neeg; ++i) {
490 fwd.info.chs.append(m_listEegChs[i]);
491 fwd.info.ch_names.append(m_listEegChs[i].ch_name);
492 }
493
494 // Bad channels
495 if (!m_pSettings->measname.isEmpty()) {
496 QFile fileBad(m_pSettings->measname);
497 FiffStream::SPtr t_pStreamBads(new FiffStream(&fileBad));
498 if (t_pStreamBads->open()) {
499 fwd.info.bads = t_pStreamBads->read_bad_channels(t_pStreamBads->dirtree());
500 t_pStreamBads->close();
501 }
502 }
503}
504
505//=============================================================================================================
506
507std::unique_ptr<MNEForwardSolution> ComputeFwd::calculateFwd()
508{
509 auto fwdSolution = std::make_unique<MNEForwardSolution>();
510 populateMetadata(*fwdSolution);
511 int iNMeg = 0;
512 int iNEeg = 0;
513
514 if(m_megcoils) {
515 iNMeg = m_megcoils->ncoil();
516 }
517 if(m_eegels) {
518 iNEeg = m_eegels->ncoil();
519 }
520 if (!m_bemModel) {
521 m_pSettings->use_threads = false;
522 }
523
524 // check if source spaces are still in computation frame
525 if(m_spaces[0]->coord_frame != m_pSettings->coord_frame) {
526 if (MNESourceSpace::transform_source_spaces_to(m_pSettings->coord_frame,m_mri_head_t,m_spaces) != OK) {
527 return nullptr;
528 }
529 }
530
531 // Do the actual computation
532 if (iNMeg > 0) {
533 if ((m_bemModel->compute_forward_meg(m_spaces,
534 m_megcoils.get(),
535 m_compcoils.get(),
536 m_compData.get(),
537 m_pSettings->fixed_ori,
538 m_pSettings->r0,
539 m_pSettings->use_threads,
540 *m_meg_forward.data(),
541 *m_meg_forward_grad.data(),
542 m_pSettings->compute_grad)) == FAIL) {
543 return nullptr;
544 }
545 }
546 if (iNEeg > 0) {
547 if ((m_bemModel->compute_forward_eeg(m_spaces,
548 m_eegels.get(),
549 m_pSettings->fixed_ori,
550 m_eegModel.get(),
551 m_pSettings->use_threads,
552 *m_eeg_forward.data(),
553 *m_eeg_forward_grad.data(),
554 m_pSettings->compute_grad))== FAIL) {
555 return nullptr;
556 }
557 }
558
559 // Assemble combined sol
560 if(iNMeg > 0 && iNEeg > 0) {
561 if(m_meg_forward->data.cols() != m_eeg_forward->data.cols()) {
562 qWarning() << "The MEG and EEG forward solutions do not match";
563 return nullptr;
564 }
565 fwdSolution->sol->clear();
566 fwdSolution->sol->nrow = m_meg_forward->nrow + m_eeg_forward->nrow;
567 fwdSolution->sol->ncol = m_meg_forward->ncol;
568 fwdSolution->sol->data = MatrixXd(fwdSolution->sol->nrow, fwdSolution->sol->ncol);
569 fwdSolution->sol->data.block(0,0,m_meg_forward->nrow,m_meg_forward->ncol) = m_meg_forward->data;
570 fwdSolution->sol->data.block(m_meg_forward->nrow,0,m_eeg_forward->nrow,m_eeg_forward->ncol) = m_eeg_forward->data;
571 fwdSolution->sol->row_names = m_meg_forward->row_names;
572 fwdSolution->sol->row_names.append(m_eeg_forward->row_names);
573 fwdSolution->sol->col_names = m_meg_forward->col_names;
574 } else if (iNMeg > 0) {
575 fwdSolution->sol = m_meg_forward;
576 } else {
577 fwdSolution->sol = m_eeg_forward;
578 }
579
580 if(m_pSettings->compute_grad) {
581 if(iNMeg > 0 && iNEeg > 0) {
582 if(m_meg_forward_grad->data.cols() != m_eeg_forward_grad->data.cols()) {
583 qWarning() << "The MEG and EEG forward solutions do not match";
584 return nullptr;
585 }
586 fwdSolution->sol_grad->clear();
587 fwdSolution->sol_grad->nrow = m_meg_forward_grad->nrow + m_eeg_forward_grad->nrow;
588 fwdSolution->sol_grad->ncol = m_meg_forward_grad->ncol;
589 fwdSolution->sol_grad->data = MatrixXd(fwdSolution->sol_grad->nrow, fwdSolution->sol_grad->ncol);
590 fwdSolution->sol_grad->data.block(0,0,m_meg_forward_grad->nrow,m_meg_forward_grad->ncol) = m_meg_forward_grad->data;
591 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;
592 fwdSolution->sol_grad->row_names = m_meg_forward_grad->row_names;
593 fwdSolution->sol_grad->row_names.append(m_eeg_forward_grad->row_names);
594 fwdSolution->sol_grad->col_names = m_meg_forward_grad->col_names;
595 } else if (iNMeg > 0) {
596 fwdSolution->sol_grad = m_meg_forward_grad;
597 } else {
598 fwdSolution->sol_grad = m_eeg_forward_grad;
599 }
600 }
601
602 return fwdSolution;
603}
604
605//=============================================================================================================
606
608{
609 int iNMeg = 0;
610 if(m_megcoils) {
611 iNMeg = m_megcoils->ncoil();
612 }
613
614 int iNComp = 0;
615 if(m_compcoils) {
616 iNComp = m_compcoils->ncoil();
617 }
618
619 // create new coilset with updated head position
620 if (m_pSettings->coord_frame == FIFFV_COORD_MRI) {
621 FiffCoordTrans head_mri_t = m_mri_head_t.inverted();
623 if (meg_mri_t.isEmpty()) {
624 return false;
625 }
626 m_megcoils = m_templates->create_meg_coils(m_listMegChs,
627 iNMeg,
629 meg_mri_t);
630 if (!m_megcoils) {
631 return false;
632 }
633 if (iNComp > 0) {
634 m_compcoils = m_templates->create_meg_coils(m_listCompChs,
635 iNComp,
637 meg_mri_t);
638 if (!m_compcoils) {
639 return false;
640 }
641 }
642 } else {
643 m_megcoils = m_templates->create_meg_coils(m_listMegChs,
644 iNMeg,
646 transDevHead);
647 if (!m_megcoils) {
648 return false;
649 }
650
651 if (iNComp > 0) {
652 m_compcoils = m_templates->create_meg_coils(m_listCompChs,
653 iNComp,
654 FWD_COIL_ACCURACY_NORMAL,transDevHead);
655 if (!m_compcoils) {
656 return false;
657 }
658 }
659 }
660
661 // check if source spaces are still in computation frame
662 if(m_spaces[0]->coord_frame != m_pSettings->coord_frame) {
663 if (MNESourceSpace::transform_source_spaces_to(m_pSettings->coord_frame,m_mri_head_t,m_spaces) != OK) {
664 return false;
665 }
666 }
667
668 // recompute meg forward
669 if ((m_bemModel->compute_forward_meg(m_spaces,
670 m_megcoils.get(),
671 m_compcoils.get(),
672 m_compData.get(),
673 m_pSettings->fixed_ori,
674 m_pSettings->r0,
675 m_pSettings->use_threads,
676 *m_meg_forward.data(),
677 *m_meg_forward_grad.data(),
678 m_pSettings->compute_grad)) == FAIL) {
679 return false;
680 }
681
682 // Update transformation matrix and info
683 m_meg_head_t = transDevHead;
684 fwd.info.dev_head_t = transDevHead;
685
686 // update solution
687 fwd.sol->data.block(0,0,m_meg_forward->nrow,m_meg_forward->ncol) = m_meg_forward->data;
688 if(m_pSettings->compute_grad) {
689 fwd.sol_grad->data.block(0,0,m_meg_forward_grad->nrow,m_meg_forward_grad->ncol) = m_meg_forward_grad->data;
690 }
691
692 return true;
693}
Top-level driver that assembles the MEG/EEG lead-field matrix G from a source space,...
constexpr int FAIL
constexpr int Y
constexpr int Z
constexpr int OK
constexpr int X
Forward solution (gain matrix mapping source dipoles to sensor measurements).
return FiffCoordTrans(from_frame, to_frame, R, moveVec)
#define FIFFV_COORD_DEVICE
#define FIFFV_COORD_HEAD
#define FIFFV_MNE_FIXED_ORI
#define FIFFV_COORD_MRI
#define FIFFV_MNE_FREE_ORI
Full FIFF measurement metadata: everything from FIFFB_MEAS / FIFFB_MEAS_INFO needed to interpret a re...
FIFF binary tag-stream layer: wraps a QIODevice to read and write FIFF tags, directories,...
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...
Definition compute_fwd.h:83
constexpr int FWD_COIL_ACCURACY_NORMAL
Definition fwd_coil.h:76
constexpr int FWD_COIL_ACCURACY_ACCURATE
Definition fwd_coil.h:77
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.
Definition fiff_id.h:66
QList< FiffChInfo > chs
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 FwdEegSphereModelSet * fwd_load_eeg_sphere_models(const QString &p_sFileName, FwdEegSphereModelSet *now)
static std::unique_ptr< MNECTFCompDataSet > read(const QString &name)
In-memory representation of an -fwd.fif forward solution.
MNELIB::MNESourceSpaces src
FIFFLIB::FiffCoordTrans mri_head_t
FIFFLIB::FiffNamedMatrix::SDPtr sol_grad
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)