27#include <QCoreApplication>
60 , m_pSettings(pSettings)
75void ComputeFwd::initFwd()
83 m_listMegChs = QList<FiffChInfo>();
84 m_listEegChs = QList<FiffChInfo>();
85 m_listCompChs = QList<FiffChInfo>();
103 QTextStream *filteredStream =
nullptr;
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());
113 qInfo(
"MRI and head coordinates are assumed to be identical.");
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());
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) {
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.reset(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?)");
208 else if (!m_pSettings->transname.isEmpty()) {
217 m_mri_head_t.print();
221 if(!m_pSettings->pFiffInfo) {
223 QFile measname(m_pSettings->measname);
226 FIFFLIB::FiffInfo fiffInfo;
227 if(!pStream->open()) {
228 qCritical() <<
"Could not open Stream.";
232 if(!pStream->read_meas_info(pStream->dirtree(), fiffInfo, DirNode)){
233 qCritical() <<
"Could not find the channel information.";
237 m_pInfoBase = QSharedPointer<FIFFLIB::FiffInfo>(
new FiffInfo(fiffInfo));
239 m_pInfoBase = m_pSettings->pFiffInfo;
242 qCritical (
"ComputeFwd::initFwd(): no FiffInfo");
245 m_pInfoBase->mne_read_meg_comp_eeg_ch_info(m_listMegChs,
253 if (!m_pSettings->meg_head_t.isEmpty()) {
254 m_meg_head_t = m_pSettings->meg_head_t;
256 if (m_meg_head_t.isEmpty()) {
257 qCritical(
"MEG -> head coordinate transformation not found.");
261 m_iNChan = iNMeg + iNEeg;
264 qInfo(
"Read %3d MEG channels from %s",iNMeg,m_pSettings->measname.toUtf8().constData());
267 qInfo(
"Read %3d MEG compensation channels from %s",iNComp,m_pSettings->measname.toUtf8().constData());
270 qInfo(
"Read %3d EEG channels from %s",iNEeg,m_pSettings->measname.toUtf8().constData());
272 if (!m_pSettings->include_meg) {
273 qInfo(
"MEG not requested. MEG channels omitted.");
274 m_listMegChs.clear();
275 m_listCompChs.clear();
280 m_meg_head_t.print();
281 if (!m_pSettings->include_eeg) {
282 qInfo(
"EEG not requested. EEG channels omitted.");
283 m_listEegChs.clear();
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";
312 if (m_compData->ncomp > 0) {
313 qInfo(
"%d compensation data sets in %s",m_compData->ncomp,m_pSettings->measname.toUtf8().constData());
315 m_listCompChs.clear();
327 m_megcoils = m_templates->create_meg_coils(m_listMegChs,
335 m_compcoils = m_templates->create_meg_coils(m_listCompChs,
350 qInfo(
"MRI coordinate coil definitions created.");
352 m_megcoils = m_templates->create_meg_coils(m_listMegChs,
361 m_compcoils = m_templates->create_meg_coils(m_listCompChs,
373 qInfo(
"Head coordinate coil definitions created.");
382 qInfo(
"Source spaces are now in %s coordinates.",FiffCoordTrans::frame_name(m_pSettings->coord_frame).toUtf8().constData());
386 if (!m_pSettings->bemname.isEmpty()) {
388 m_pSettings->bemname = bemsolname;
390 qInfo(
"Setting up the BEM model using %s...",m_pSettings->bemname.toUtf8().constData());
391 qInfo(
"Loading surfaces...");
395 qInfo(
"Three-layer model surfaces loaded.");
402 qInfo(
"Homogeneous model surface loaded.");
404 if (iNEeg > 0 && m_bemModel->nsurf == 1) {
405 qCritical(
"Cannot use a homogeneous model in EEG calculations.");
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) {
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) {
418 qInfo(
"BEM model %s is now set up",m_bemModel->sol_name.toUtf8().constData());
420 qInfo(
"Using the sphere model.");
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;
432 filteredStream =
new QTextStream(&filteredFile);
433 qInfo(
"Omitted source space points will be output to : %s",m_pSettings->mindistoutname.toUtf8().constData());
436 m_pSettings->bemname,
439 filteredStream,m_pSettings->use_threads);
440 delete filteredStream;
441 filteredStream =
nullptr;
447void ComputeFwd::populateMetadata(MNEForwardSolution& fwd)
454 int nmeg = m_megcoils ? m_megcoils->ncoil() : 0;
455 int neeg = m_eegels ? m_eegels->ncoil() : 0;
456 fwd.
nchan = nmeg + neeg;
468 for (
int i = 0; i < static_cast<int>(m_spaces.size()); ++i) {
470 fwd.
nsource += m_spaces[i]->nuse;
485 for (
int i = 0; i < nmeg; ++i) {
486 fwd.
info.
chs.append(m_listMegChs[i]);
489 for (
int i = 0; i < neeg; ++i) {
490 fwd.
info.
chs.append(m_listEegChs[i]);
495 if (!m_pSettings->measname.isEmpty()) {
496 QFile fileBad(m_pSettings->measname);
498 if (t_pStreamBads->open()) {
499 fwd.
info.
bads = t_pStreamBads->read_bad_channels(t_pStreamBads->dirtree());
500 t_pStreamBads->close();
509 auto fwdSolution = std::make_unique<MNEForwardSolution>();
510 populateMetadata(*fwdSolution);
515 iNMeg = m_megcoils->ncoil();
518 iNEeg = m_eegels->ncoil();
521 m_pSettings->use_threads =
false;
525 if(m_spaces[0]->coord_frame != m_pSettings->coord_frame) {
533 if ((m_bemModel->compute_forward_meg(m_spaces,
537 m_pSettings->fixed_ori,
539 m_pSettings->use_threads,
540 *m_meg_forward.data(),
541 *m_meg_forward_grad.data(),
542 m_pSettings->compute_grad)) ==
FAIL) {
547 if ((m_bemModel->compute_forward_eeg(m_spaces,
549 m_pSettings->fixed_ori,
551 m_pSettings->use_threads,
552 *m_eeg_forward.data(),
553 *m_eeg_forward_grad.data(),
554 m_pSettings->compute_grad))==
FAIL) {
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";
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;
577 fwdSolution->sol = m_eeg_forward;
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";
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;
598 fwdSolution->sol_grad = m_eeg_forward_grad;
611 iNMeg = m_megcoils->ncoil();
616 iNComp = m_compcoils->ncoil();
626 m_megcoils = m_templates->create_meg_coils(m_listMegChs,
634 m_compcoils = m_templates->create_meg_coils(m_listCompChs,
643 m_megcoils = m_templates->create_meg_coils(m_listMegChs,
652 m_compcoils = m_templates->create_meg_coils(m_listCompChs,
662 if(m_spaces[0]->coord_frame != m_pSettings->coord_frame) {
669 if ((m_bemModel->compute_forward_meg(m_spaces,
673 m_pSettings->fixed_ori,
675 m_pSettings->use_threads,
676 *m_meg_forward.data(),
677 *m_meg_forward_grad.data(),
678 m_pSettings->compute_grad)) ==
FAIL) {
683 m_meg_head_t = transDevHead;
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;
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).
return FiffCoordTrans(from_frame, to_frame, R, moveVec)
#define FIFFV_COORD_DEVICE
#define FIFFV_MNE_FIXED_ORI
#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...
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 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.
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
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)