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