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_pSettings(pSettings)
57, m_meg_forward(new FiffNamedMatrix)
58, m_meg_forward_grad(new FiffNamedMatrix)
59, m_eeg_forward(new FiffNamedMatrix)
60, m_eeg_forward_grad(new FiffNamedMatrix)
61{
62 initFwd();
63}
64
65//=============================================================================================================
66
70
71//=============================================================================================================
72
73//=============================================================================================================
74
75void ComputeFwd::initFwd()
76{
77 m_bInitialized = false;
78 m_spaces.clear();
79 m_iNSource = 0;
80
81 m_mri_head_t = FiffCoordTrans();
82 m_meg_head_t = FiffCoordTrans();
83
84 m_listMegChs = QList<FiffChInfo>();
85 m_listEegChs = QList<FiffChInfo>();
86 m_listCompChs = QList<FiffChInfo>();
87
88 int iNMeg = 0;
89 int iNEeg = 0;
90 int iNComp = 0;
91
92 m_templates.reset();
93 m_megcoils.reset();
94 m_compcoils.reset();
95 m_eegels.reset();
96 m_eegModels.reset();
97 m_iNChan = 0;
98
99 int k;
100 m_mri_id = FiffId();
101 m_meas_id.clear();
102
103 QFile filteredFile;
104 QTextStream* filteredStream = nullptr;
105
106 m_eegModel.reset();
107 m_bemModel.reset();
108
109 // Report the setup
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());
113 } else {
114 qInfo("MRI and head coordinates are assumed to be identical.");
115 }
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());
119 } else {
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.");
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 } else if (!m_pSettings->transname.isEmpty()) {
208 FiffCoordTrans t = FiffCoordTrans::readFShead2mriTransform(m_pSettings->transname.toUtf8().data());
209 if (t.isEmpty()) {
210 return;
211 }
212 m_mri_head_t = t.inverted();
213 } else {
215 }
216 m_mri_head_t.print();
217
218 // Read the channel information and the MEG device -> head coordinate transformation
219
220 if (!m_pSettings->pFiffInfo) {
221 QFile measname(m_pSettings->measname);
223 FiffStream::SPtr pStream(new FiffStream(&measname));
224 FIFFLIB::FiffInfo fiffInfo;
225 if (!pStream->open()) {
226 qCritical() << "Could not open Stream.";
227 return;
228 }
229
230 if (!pStream->read_meas_info(pStream->dirtree(), fiffInfo, DirNode)) {
231 qCritical() << "Could not find the channel information.";
232 return;
233 }
234 pStream->close();
235 m_pInfoBase = QSharedPointer<FIFFLIB::FiffInfo>(new FiffInfo(fiffInfo));
236 } else {
237 m_pInfoBase = m_pSettings->pFiffInfo;
238 }
239 if (!m_pInfoBase) {
240 qCritical("ComputeFwd::initFwd(): no FiffInfo");
241 return;
242 }
243 m_pInfoBase->mne_read_meg_comp_eeg_ch_info(m_listMegChs,
244 iNMeg,
245 m_listCompChs,
246 iNComp,
247 m_listEegChs,
248 iNEeg,
249 m_meg_head_t,
250 m_meas_id);
251 if (!m_pSettings->meg_head_t.isEmpty()) {
252 m_meg_head_t = m_pSettings->meg_head_t;
253 }
254 if (m_meg_head_t.isEmpty()) {
255 qCritical("MEG -> head coordinate transformation not found.");
256 return;
257 }
258
259 m_iNChan = iNMeg + iNEeg;
260
261 if (iNMeg > 0) {
262 qInfo("Read %3d MEG channels from %s", iNMeg, m_pSettings->measname.toUtf8().constData());
263 }
264 if (iNComp > 0) {
265 qInfo("Read %3d MEG compensation channels from %s", iNComp, m_pSettings->measname.toUtf8().constData());
266 }
267 if (iNEeg > 0) {
268 qInfo("Read %3d EEG channels from %s", iNEeg, m_pSettings->measname.toUtf8().constData());
269 }
270 if (!m_pSettings->include_meg) {
271 qInfo("MEG not requested. MEG channels omitted.");
272 m_listMegChs.clear();
273 m_listCompChs.clear();
274 iNMeg = 0;
275 iNComp = 0;
276 } else
277 m_meg_head_t.print();
278 if (!m_pSettings->include_eeg) {
279 qInfo("EEG not requested. EEG channels omitted.");
280 m_listEegChs.clear();
281 iNEeg = 0;
282 } else {
283 if (!FiffChInfo::checkEegLocations(m_listEegChs, iNEeg)) {
284 return;
285 }
286 }
287
288 // Create coil descriptions with transformation to head or MRI frame
289
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";
296 }
297
298 m_templates = FwdCoilSet::read_coil_defs(m_qPath);
299 if (!m_templates) {
300 return;
301 }
302
303 // Compensation data
304
305 m_compData = MNECTFCompDataSet::read(m_pSettings->measname);
306 if (!m_compData) {
307 return;
308 }
309 if (m_compData->ncomp > 0) {
310 qInfo("%d compensation data sets in %s", m_compData->ncomp, m_pSettings->measname.toUtf8().constData());
311 } else {
312 m_listCompChs.clear();
313 iNComp = 0;
314
315 m_compData.reset();
316 }
317 }
318 if (m_pSettings->coord_frame == FIFFV_COORD_MRI) {
319 FiffCoordTrans head_mri_t = m_mri_head_t.inverted();
320 FiffCoordTrans meg_mri_t = FiffCoordTrans::combine(FIFFV_COORD_DEVICE, FIFFV_COORD_MRI, m_meg_head_t, head_mri_t);
321 if (meg_mri_t.isEmpty()) {
322 return;
323 }
324 m_megcoils = m_templates->create_meg_coils(m_listMegChs,
325 iNMeg,
327 meg_mri_t);
328 if (!m_megcoils) {
329 return;
330 }
331 if (iNComp > 0) {
332 m_compcoils = m_templates->create_meg_coils(m_listCompChs,
333 iNComp,
335 meg_mri_t);
336 if (!m_compcoils) {
337 return;
338 }
339 }
340 m_eegels = FwdCoilSet::create_eeg_els(m_listEegChs,
341 iNEeg,
342 head_mri_t);
343 if (!m_eegels) {
344 return;
345 }
346
347 qInfo("MRI coordinate coil definitions created.");
348 } else {
349 m_megcoils = m_templates->create_meg_coils(m_listMegChs,
350 iNMeg,
352 m_meg_head_t);
353 if (!m_megcoils) {
354 return;
355 }
356
357 if (iNComp > 0) {
358 m_compcoils = m_templates->create_meg_coils(m_listCompChs,
359 iNComp,
360 FWD_COIL_ACCURACY_NORMAL, m_meg_head_t);
361 if (!m_compcoils) {
362 return;
363 }
364 }
365 m_eegels = FwdCoilSet::create_eeg_els(m_listEegChs,
366 iNEeg);
367 if (!m_eegels) {
368 return;
369 }
370 qInfo("Head coordinate coil definitions created.");
371 }
372
373 // Transform the source spaces into the appropriate coordinates
374 {
375 if (MNESourceSpace::transform_source_spaces_to(m_pSettings->coord_frame, m_mri_head_t, m_spaces) != OK) {
376 return;
377 }
378 }
379 qInfo("Source spaces are now in %s coordinates.", FiffCoordTrans::frame_name(m_pSettings->coord_frame).toUtf8().constData());
380
381 // Prepare the BEM model if necessary
382
383 if (!m_pSettings->bemname.isEmpty()) {
384 QString bemsolname = FwdBemModel::fwd_bem_make_bem_sol_name(m_pSettings->bemname);
385 m_pSettings->bemname = bemsolname;
386
387 qInfo("Setting up the BEM model using %s...", m_pSettings->bemname.toUtf8().constData());
388 qInfo("Loading surfaces...");
389 m_bemModel = FwdBemModel::fwd_bem_load_three_layer_surfaces(m_pSettings->bemname);
390
391 if (m_bemModel) {
392 qInfo("Three-layer model surfaces loaded.");
393 } else {
394 m_bemModel = FwdBemModel::fwd_bem_load_homog_surface(m_pSettings->bemname);
395 if (!m_bemModel) {
396 return;
397 }
398 qInfo("Homogeneous model surface loaded.");
399 }
400 if (iNEeg > 0 && m_bemModel->nsurf == 1) {
401 qCritical("Cannot use a homogeneous model in EEG calculations.");
402 return;
403 }
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) {
406 return;
407 }
408 if (m_pSettings->coord_frame == FIFFV_COORD_HEAD) {
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) {
411 return;
412 }
413 }
414 qInfo("BEM model %s is now set up", m_bemModel->sol_name.toUtf8().constData());
415 } else {
416 qInfo("Using the sphere model.");
417 m_bemModel = std::make_unique<FwdBemModel>(); // no surfaces selects the sphere branch
418 }
419
420 // Try to circumvent numerical problems by excluding points too close or outside the inner skull surface
421
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;
427 return;
428 }
429 filteredStream = new QTextStream(&filteredFile);
430 qInfo("Omitted source space points will be output to : %s", m_pSettings->mindistoutname.toUtf8().constData());
431 }
432 MNESourceSpace::filter_source_spaces(m_pSettings->mindist,
433 m_pSettings->bemname,
434 m_mri_head_t,
435 m_spaces,
436 filteredStream, m_pSettings->use_threads);
437 delete filteredStream;
438 filteredStream = nullptr;
439 }
440 m_bInitialized = true;
441}
442
443//=============================================================================================================
444
445void ComputeFwd::populateMetadata(MNEForwardSolution& fwd)
446{
447 fwd.coord_frame = m_pSettings->coord_frame;
448 fwd.source_ori = m_pSettings->fixed_ori ? FIFFV_MNE_FIXED_ORI : FIFFV_MNE_FREE_ORI;
449 fwd.surf_ori = false;
450 fwd.mri_filename = m_pSettings->mriname;
451
452 int nmeg = m_megcoils ? m_megcoils->ncoil() : 0;
453 int neeg = m_eegels ? m_eegels->ncoil() : 0;
454 fwd.nchan = nmeg + neeg;
455
456 fwd.mri_head_t = m_mri_head_t;
457 fwd.mri_id = m_mri_id;
458
459 // Source spaces: store in MRI frame (FIFF convention), keep m_spaces in computation frame
460 {
461 if (MNESourceSpace::transform_source_spaces_to(FIFFV_COORD_MRI, m_mri_head_t, m_spaces) != OK) {
462 return;
463 }
464 fwd.src.clear();
465 fwd.nsource = 0;
466 for (int i = 0; i < static_cast<int>(m_spaces.size()); ++i) {
467 fwd.src.append(*m_spaces[i]);
468 fwd.nsource += m_spaces[i]->nuse;
469 }
470 if (MNESourceSpace::transform_source_spaces_to(m_pSettings->coord_frame, m_mri_head_t, m_spaces) != OK) {
471 return;
472 }
473 }
474
475 // Source locations and orientations in the computation frame, as MNEForwardSolution::read sets them.
476 const int nOri = m_pSettings->fixed_ori ? 1 : 3;
477 fwd.source_rr = MatrixX3f(fwd.nsource, 3);
478 fwd.source_nn = MatrixX3f(nOri * fwd.nsource, 3);
479 for (int i = 0, p = 0; i < static_cast<int>(m_spaces.size()); ++i) {
480 const MNESourceSpace& s = *m_spaces[i];
481 for (int k = 0; k < s.nuse; ++k, ++p) {
482 fwd.source_rr.row(p) = s.rr.row(s.vertno(k));
483 if (nOri == 1) {
484 fwd.source_nn.row(p) = s.nn.row(s.vertno(k));
485 } else {
486 fwd.source_nn.middleRows(3 * p, 3) = Matrix3f::Identity();
487 }
488 }
489 }
490
491 // Measurement provenance
492 fwd.info.filename = m_pSettings->measname;
493 fwd.info.meas_id = m_meas_id;
494 fwd.info.dev_head_t = m_meg_head_t;
495 fwd.info.nchan = fwd.nchan;
496
497 fwd.info.chs.clear();
498 fwd.info.ch_names.clear();
499 for (int i = 0; i < nmeg; ++i) {
500 fwd.info.chs.append(m_listMegChs[i]);
501 fwd.info.ch_names.append(m_listMegChs[i].ch_name);
502 }
503 for (int i = 0; i < neeg; ++i) {
504 fwd.info.chs.append(m_listEegChs[i]);
505 fwd.info.ch_names.append(m_listEegChs[i].ch_name);
506 }
507
508 // Bad channels
509 if (!m_pSettings->measname.isEmpty()) {
510 QFile fileBad(m_pSettings->measname);
511 FiffStream::SPtr t_pStreamBads(new FiffStream(&fileBad));
512 if (t_pStreamBads->open()) {
513 fwd.info.bads = t_pStreamBads->read_bad_channels(t_pStreamBads->dirtree());
514 t_pStreamBads->close();
515 }
516 }
517}
518
519//=============================================================================================================
520
521std::unique_ptr<MNEForwardSolution> ComputeFwd::calculateFwd()
522{
523 if (!m_bInitialized) {
524 qCritical("ComputeFwd::calculateFwd - the forward computation could not be set up.");
525 return nullptr;
526 }
527 auto fwdSolution = std::make_unique<MNEForwardSolution>();
528 populateMetadata(*fwdSolution);
529 int iNMeg = 0;
530 int iNEeg = 0;
531
532 if (m_megcoils) {
533 iNMeg = m_megcoils->ncoil();
534 }
535 if (m_eegels) {
536 iNEeg = m_eegels->ncoil();
537 }
538 if (!m_bemModel || m_bemModel->nsurf == 0) {
539 m_pSettings->use_threads = false;
540 }
541
542 // check if source spaces are still in computation frame
543 if (m_spaces[0]->coord_frame != m_pSettings->coord_frame) {
544 if (MNESourceSpace::transform_source_spaces_to(m_pSettings->coord_frame, m_mri_head_t, m_spaces) != OK) {
545 return nullptr;
546 }
547 }
548
549 // Do the actual computation
550 if (iNMeg > 0) {
551 if ((m_bemModel->compute_forward_meg(m_spaces,
552 m_megcoils.get(),
553 m_compcoils.get(),
554 m_compData.get(),
555 m_pSettings->fixed_ori,
556 m_pSettings->r0,
557 m_pSettings->use_threads,
558 *m_meg_forward.data(),
559 *m_meg_forward_grad.data(),
560 m_pSettings->compute_grad)) == FAIL) {
561 return nullptr;
562 }
563 }
564 if (iNEeg > 0) {
565 if ((m_bemModel->compute_forward_eeg(m_spaces,
566 m_eegels.get(),
567 m_pSettings->fixed_ori,
568 m_eegModel.get(),
569 m_pSettings->use_threads,
570 *m_eeg_forward.data(),
571 *m_eeg_forward_grad.data(),
572 m_pSettings->compute_grad)) == FAIL) {
573 return nullptr;
574 }
575 }
576
577 // Assemble combined sol
578 if (iNMeg > 0 && iNEeg > 0) {
579 if (m_meg_forward->data.cols() != m_eeg_forward->data.cols()) {
580 qWarning() << "The MEG and EEG forward solutions do not match";
581 return nullptr;
582 }
583 fwdSolution->sol->clear();
584 fwdSolution->sol->nrow = m_meg_forward->nrow + m_eeg_forward->nrow;
585 fwdSolution->sol->ncol = m_meg_forward->ncol;
586 fwdSolution->sol->data = MatrixXd(fwdSolution->sol->nrow, fwdSolution->sol->ncol);
587 fwdSolution->sol->data.block(0, 0, m_meg_forward->nrow, m_meg_forward->ncol) = m_meg_forward->data;
588 fwdSolution->sol->data.block(m_meg_forward->nrow, 0, m_eeg_forward->nrow, m_eeg_forward->ncol) = m_eeg_forward->data;
589 fwdSolution->sol->row_names = m_meg_forward->row_names;
590 fwdSolution->sol->row_names.append(m_eeg_forward->row_names);
591 fwdSolution->sol->col_names = m_meg_forward->col_names;
592 } else if (iNMeg > 0) {
593 fwdSolution->sol = m_meg_forward;
594 } else {
595 fwdSolution->sol = m_eeg_forward;
596 }
597
598 if (m_pSettings->compute_grad) {
599 if (iNMeg > 0 && iNEeg > 0) {
600 if (m_meg_forward_grad->data.cols() != m_eeg_forward_grad->data.cols()) {
601 qWarning() << "The MEG and EEG forward solutions do not match";
602 return nullptr;
603 }
604 fwdSolution->sol_grad->clear();
605 fwdSolution->sol_grad->nrow = m_meg_forward_grad->nrow + m_eeg_forward_grad->nrow;
606 fwdSolution->sol_grad->ncol = m_meg_forward_grad->ncol;
607 fwdSolution->sol_grad->data = MatrixXd(fwdSolution->sol_grad->nrow, fwdSolution->sol_grad->ncol);
608 fwdSolution->sol_grad->data.block(0, 0, m_meg_forward_grad->nrow, m_meg_forward_grad->ncol) = m_meg_forward_grad->data;
609 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;
610 fwdSolution->sol_grad->row_names = m_meg_forward_grad->row_names;
611 fwdSolution->sol_grad->row_names.append(m_eeg_forward_grad->row_names);
612 fwdSolution->sol_grad->col_names = m_meg_forward_grad->col_names;
613 } else if (iNMeg > 0) {
614 fwdSolution->sol_grad = m_meg_forward_grad;
615 } else {
616 fwdSolution->sol_grad = m_eeg_forward_grad;
617 }
618 }
619
620 return fwdSolution;
621}
622
623//=============================================================================================================
624
626{
627 if (!m_bInitialized)
628 return false;
629 int iNMeg = 0;
630 if (m_megcoils) {
631 iNMeg = m_megcoils->ncoil();
632 }
633
634 int iNComp = 0;
635 if (m_compcoils) {
636 iNComp = m_compcoils->ncoil();
637 }
638
639 // create new coilset with updated head position
640 if (m_pSettings->coord_frame == FIFFV_COORD_MRI) {
641 FiffCoordTrans head_mri_t = m_mri_head_t.inverted();
642 FiffCoordTrans meg_mri_t = FiffCoordTrans::combine(FIFFV_COORD_DEVICE, FIFFV_COORD_MRI, transDevHead, head_mri_t);
643 if (meg_mri_t.isEmpty()) {
644 return false;
645 }
646 m_megcoils = m_templates->create_meg_coils(m_listMegChs,
647 iNMeg,
649 meg_mri_t);
650 if (!m_megcoils) {
651 return false;
652 }
653 if (iNComp > 0) {
654 m_compcoils = m_templates->create_meg_coils(m_listCompChs,
655 iNComp,
657 meg_mri_t);
658 if (!m_compcoils) {
659 return false;
660 }
661 }
662 } else {
663 m_megcoils = m_templates->create_meg_coils(m_listMegChs,
664 iNMeg,
666 transDevHead);
667 if (!m_megcoils) {
668 return false;
669 }
670
671 if (iNComp > 0) {
672 m_compcoils = m_templates->create_meg_coils(m_listCompChs,
673 iNComp,
674 FWD_COIL_ACCURACY_NORMAL, transDevHead);
675 if (!m_compcoils) {
676 return false;
677 }
678 }
679 }
680
681 // check if source spaces are still in computation frame
682 if (m_spaces[0]->coord_frame != m_pSettings->coord_frame) {
683 if (MNESourceSpace::transform_source_spaces_to(m_pSettings->coord_frame, m_mri_head_t, m_spaces) != OK) {
684 return false;
685 }
686 }
687
688 // recompute meg forward
689 if ((m_bemModel->compute_forward_meg(m_spaces,
690 m_megcoils.get(),
691 m_compcoils.get(),
692 m_compData.get(),
693 m_pSettings->fixed_ori,
694 m_pSettings->r0,
695 m_pSettings->use_threads,
696 *m_meg_forward.data(),
697 *m_meg_forward_grad.data(),
698 m_pSettings->compute_grad)) == FAIL) {
699 return false;
700 }
701
702 // Update transformation matrix and info
703 m_meg_head_t = transDevHead;
704 fwd.info.dev_head_t = transDevHead;
705
706 // update solution
707 fwd.sol->data.block(0, 0, m_meg_forward->nrow, m_meg_forward->ncol) = m_meg_forward->data;
708 if (m_pSettings->compute_grad) {
709 fwd.sol_grad->data.block(0, 0, m_meg_forward_grad->nrow, m_meg_forward_grad->ncol) = m_meg_forward_grad->data;
710 }
711
712 return true;
713}
#define FIFFV_COORD_DEVICE
#define FIFFV_COORD_HEAD
#define FIFFV_MNE_FIXED_ORI
#define FIFFV_COORD_MRI
#define FIFFV_MNE_FREE_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,...
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).
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:85
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:69
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)