v2.0.0
Loading...
Searching...
No Matches
fwd_coil_set.cpp
Go to the documentation of this file.
1//=============================================================================================================
36
37//=============================================================================================================
38// INCLUDES
39//=============================================================================================================
40
41#include "fwd_coil_set.h"
42#include "fwd_coil.h"
43#include "fwd_bem_solution.h"
44
45#include <fiff/fiff_ch_info.h>
46
47//=============================================================================================================
48// QT INCLUDES
49//=============================================================================================================
50
51#include <QDebug>
52#include <QFile>
53#include <QLocale>
54#include <QTextStream>
55
56//=============================================================================================================
57// USED NAMESPACES
58//=============================================================================================================
59
60using namespace Eigen;
61using namespace FIFFLIB;
62using namespace FWDLIB;
63
64namespace {
65constexpr float BIG = 0.5f;
66}
67
71static QString readWord(QTextStream &in)
72{
73 in.skipWhiteSpace();
74 if (in.atEnd()) return QString();
75
76 QChar ch;
77 in >> ch;
78
79 if (ch == '"') {
80 QString word;
81 while (!in.atEnd()) {
82 in >> ch;
83 if (ch == '"') break;
84 word += ch;
85 }
86 return word;
87 }
88
89 QString word(ch);
90 while (!in.atEnd()) {
91 in >> ch;
92 if (ch.isSpace()) break;
93 word += ch;
94 }
95 return word;
96}
97
98FwdCoil* FwdCoilSet::fwd_add_coil_to_set(int type, int coil_class, int acc, int np, float size, float base, const QString& desc)
99{
100 if (np <= 0) {
101 qWarning("Number of integration points should be positive (type = %d acc = %d)",type,acc);
102 return nullptr;
103 }
104 if (! (acc == FWD_COIL_ACCURACY_POINT ||
107 qWarning("Illegal accuracy (type = %d acc = %d)",type,acc);
108 return nullptr;
109 }
110 if (! (coil_class == FWD_COILC_MAG ||
111 coil_class == FWD_COILC_AXIAL_GRAD ||
112 coil_class == FWD_COILC_PLANAR_GRAD ||
113 coil_class == FWD_COILC_AXIAL_GRAD2) ) {
114 qWarning("Illegal coil class (type = %d acc = %d class = %d)",type,acc,coil_class);
115 return nullptr;
116 }
117
118 coils.push_back(std::make_unique<FwdCoil>(np));
119 FwdCoil* def = coils.back().get();
120
121 def->type = type;
122 def->coil_class = coil_class;
123 def->accuracy = acc;
124 def->np = np;
125 def->size = size;
126 def->base = base;
127 if (!desc.isEmpty())
128 def->desc = desc;
129 return def;
130}
131
132//=============================================================================================================
133// DEFINE MEMBER METHODS
134//=============================================================================================================
135
140
141//=============================================================================================================
142
146
147//=============================================================================================================
148
150{
151 if (ch.kind != FIFFV_MEG_CH && ch.kind != FIFFV_REF_MEG_CH) {
152 qWarning() << ch.ch_name << "is not a MEG channel. Cannot create a coil definition.";
153 return nullptr;
154 }
155 /*
156 * Simple linear search from the coil definitions
157 */
158 FwdCoil* def = nullptr;
159 for (int k = 0; k < this->ncoil(); k++) {
160 if ((this->coils[k]->type == (ch.chpos.coil_type & 0xFFFF)) &&
161 this->coils[k]->accuracy == acc) {
162 def = this->coils[k].get();
163 }
164 }
165 if (!def) {
166 qWarning("Desired coil definition not found (type = %d acc = %d)",ch.chpos.coil_type,acc);
167 return nullptr;
168 }
169 /*
170 * Create the result
171 */
172 auto res = std::make_unique<FwdCoil>(def->np);
173
174 res->chname = ch.ch_name;
175 if (!def->desc.isEmpty())
176 res->desc = def->desc;
177 res->coil_class = def->coil_class;
178 res->accuracy = def->accuracy;
179 res->base = def->base;
180 res->size = def->size;
181 res->type = ch.chpos.coil_type;
182
183 res->r0 = ch.chpos.r0;
184 res->ex = ch.chpos.ex;
185 res->ey = ch.chpos.ey;
186 res->ez = ch.chpos.ez;
187 /*
188 * Apply a coordinate transformation if so desired
189 */
190 if (!t.isEmpty()) {
191 FiffCoordTrans::apply_trans(res->r0.data(),t,FIFFV_MOVE);
195 res->coord_frame = t.to;
196 }
197 else
198 res->coord_frame = FIFFV_COORD_DEVICE;
199
200 for (int p = 0; p < res->np; p++) {
201 res->w[p] = def->w[p];
202 res->rmag.row(p) = res->r0 + def->rmag(p, 0)*res->ex + def->rmag(p, 1)*res->ey + def->rmag(p, 2)*res->ez;
203 res->cosmag.row(p) = def->cosmag(p, 0)*res->ex + def->cosmag(p, 1)*res->ey + def->cosmag(p, 2)*res->ez;
204 }
205 return res;
206}
207
208//=============================================================================================================
209
210FwdCoilSet::UPtr FwdCoilSet::create_meg_coils(const QList<FIFFLIB::FiffChInfo>& chs,
211 int nch,
212 int acc,
213 const FiffCoordTrans& t)
214{
215 auto res = std::make_unique<FwdCoilSet>();
216
217 for (int k = 0; k < nch; k++) {
218 auto next = this->create_meg_coil(chs.at(k),acc,t);
219 if (!next)
220 return nullptr;
221 res->coils.push_back(std::move(next));
222 }
223 if (!t.isEmpty())
224 res->coord_frame = t.to;
225 return res;
226}
227
228//=============================================================================================================
229
230FwdCoilSet::UPtr FwdCoilSet::create_eeg_els(const QList<FIFFLIB::FiffChInfo>& chs,
231 int nch,
232 const FiffCoordTrans& t)
233{
234 auto res = std::make_unique<FwdCoilSet>();
235
236 for (int k = 0; k < nch; k++) {
237 auto next = FwdCoil::create_eeg_el(chs.at(k),t);
238 if (!next)
239 return nullptr;
240 res->coils.push_back(std::move(next));
241 }
242 if (!t.isEmpty())
243 res->coord_frame = t.to;
244 return res;
245}
246
247//=============================================================================================================
248
250{
251 QFile file(name);
252 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
253 qWarning() << "FwdCoilSet::read_coil_defs - Cannot open" << name;
254 return nullptr;
255 }
256
257 // Read file content, stripping comments
258 QString content;
259 {
260 QTextStream fileIn(&file);
261 while (!fileIn.atEnd()) {
262 QString line = fileIn.readLine();
263 int idx = line.indexOf('#');
264 if (idx >= 0)
265 line.truncate(idx);
266 content += line + '\n';
267 }
268 }
269 file.close();
270
271 QTextStream in(&content);
272 in.setLocale(QLocale::c());
273
274 auto res = std::make_unique<FwdCoilSet>();
275 while (!in.atEnd()) {
276 /*
277 * Read basic info
278 */
279 int coil_class;
280 in >> coil_class;
281 if (in.status() != QTextStream::Ok)
282 break;
283
284 int type, acc, np;
285 float size, base;
286
287 in >> type >> acc >> np >> size >> base;
288 if (in.status() != QTextStream::Ok) {
289 qWarning("FwdCoilSet::read_coil_defs - Error reading coil header");
290 return nullptr;
291 }
292
293 QString desc = readWord(in);
294 if (desc.isEmpty()) {
295 qWarning("FwdCoilSet::read_coil_defs - Missing coil description");
296 return nullptr;
297 }
298
299 FwdCoil* def = res->fwd_add_coil_to_set(type,coil_class,acc,np,size,base,desc);
300 if (!def)
301 return nullptr;
302
303 for (int p = 0; p < def->np; p++) {
304 /*
305 * Read and verify data for each integration point
306 */
307 in >> def->w[p]
308 >> def->rmag(p, 0) >> def->rmag(p, 1) >> def->rmag(p, 2)
309 >> def->cosmag(p, 0) >> def->cosmag(p, 1) >> def->cosmag(p, 2);
310 if (in.status() != QTextStream::Ok) {
311 qWarning("FwdCoilSet::read_coil_defs - Error reading integration point %d", p);
312 return nullptr;
313 }
314
315 if (def->pos(p).norm() > BIG) {
316 qWarning("Unreasonable integration point: %f %f %f mm (coil type = %d acc = %d)", 1000*def->rmag(p, 0),1000*def->rmag(p, 1),1000*def->rmag(p, 2), def->type,def->accuracy);
317 return nullptr;
318 }
319 float cosmagNorm = def->dir(p).norm();
320 if (cosmagNorm <= 0) {
321 qWarning("Unreasonable normal: %f %f %f (coil type = %d acc = %d)", def->cosmag(p, 0),def->cosmag(p, 1),def->cosmag(p, 2), def->type,def->accuracy);
322 return nullptr;
323 }
324 def->cosmag.row(p).normalize();
325 }
326 }
327
328 qInfo("%d coil definitions read",res->ncoil());
329 return res;
330}
331
332//=============================================================================================================
333
335{
337
338 if (!t.isEmpty()) {
339 if (this->coord_frame != t.from) {
340 qWarning("Coordinate frame of the transformation does not match the coil set in fwd_dup_coil_set");
341 return nullptr;
342 }
343 }
344 res = std::make_unique<FwdCoilSet>();
345 if (!t.isEmpty())
346 res->coord_frame = t.to;
347 else
348 res->coord_frame = this->coord_frame;
349
350 res->coils.reserve(this->ncoil());
351
352 for (int k = 0; k < this->ncoil(); k++) {
353 auto coil = std::make_unique<FwdCoil>(*(this->coils[k]));
354 /*
355 * Optional coordinate transformation
356 */
357 if (!t.isEmpty()) {
358 FiffCoordTrans::apply_trans(coil->r0.data(),t,FIFFV_MOVE);
362
363 for (int p = 0; p < coil->np; p++) {
364 FiffCoordTrans::apply_trans(&coil->rmag(p, 0),t,FIFFV_MOVE);
365 FiffCoordTrans::apply_trans(&coil->cosmag(p, 0),t,FIFFV_NO_MOVE);
366 }
367 coil->coord_frame = t.to;
368 }
369 res->coils.push_back(std::move(coil));
370 }
371 return res;
372}
373
374//=============================================================================================================
375
377{
378 if (type == FIFFV_COIL_EEG)
379 return false;
380 for (int k = 0; k < this->ncoil(); k++)
381 if (this->coils[k]->type == type)
382 return this->coils[k]->coil_class == FWD_COILC_PLANAR_GRAD;
383 return false;
384}
385
386//=============================================================================================================
387
389{
390 if (type == FIFFV_COIL_EEG)
391 return false;
392 for (int k = 0; k < this->ncoil(); k++)
393 if (this->coils[k]->type == type)
394 return (this->coils[k]->coil_class == FWD_COILC_MAG ||
395 this->coils[k]->coil_class == FWD_COILC_AXIAL_GRAD ||
396 this->coils[k]->coil_class == FWD_COILC_AXIAL_GRAD2);
397 return false;
398}
399
400//=============================================================================================================
401
403{
404 if (type == FIFFV_COIL_EEG)
405 return false;
406 for (int k = 0; k < this->ncoil(); k++)
407 if (this->coils[k]->type == type)
408 return this->coils[k]->coil_class == FWD_COILC_MAG;
409 return false;
410}
411
412//=============================================================================================================
413
415{
416 return type == FIFFV_COIL_EEG;
417}
418
#define FIFFV_COORD_DEVICE
#define FIFFV_REF_MEG_CH
#define FIFFV_MEG_CH
#define FIFFV_NO_MOVE
#define FIFFV_COORD_UNKNOWN
#define FIFFV_MOVE
#define FIFFV_COIL_EEG
FiffChInfo class declaration.
#define BIG
FwdBemSolution class declaration.
FwdCoil class declaration.
FwdCoilSet class declaration.
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_POINT
Definition fwd_coil.h:80
constexpr int FWD_COIL_ACCURACY_ACCURATE
Definition fwd_coil.h:82
constexpr int FWD_COILC_PLANAR_GRAD
Definition fwd_coil.h:77
constexpr int FWD_COILC_AXIAL_GRAD2
Definition fwd_coil.h:78
constexpr int FWD_COILC_AXIAL_GRAD
Definition fwd_coil.h:76
constexpr int FWD_COILC_MAG
Definition fwd_coil.h:75
Channel info descriptor.
Eigen::Vector3f r0
fiff_int_t coil_type
Eigen::Vector3f ey
Eigen::Vector3f ex
Eigen::Vector3f ez
Coordinate transformation description.
Single MEG or EEG sensor coil with integration points, weights, and coordinate frame.
Definition fwd_coil.h:93
std::unique_ptr< FwdCoil > UPtr
Definition fwd_coil.h:95
Eigen::Map< const Eigen::Vector3f > dir(int j) const
Definition fwd_coil.h:182
Eigen::Map< const Eigen::Vector3f > pos(int j) const
Definition fwd_coil.h:180
Eigen::Matrix< float, Eigen::Dynamic, 3, Eigen::RowMajor > cosmag
Definition fwd_coil.h:176
static FwdCoil::UPtr create_eeg_el(const FIFFLIB::FiffChInfo &ch, const FIFFLIB::FiffCoordTrans &t=FIFFLIB::FiffCoordTrans())
Definition fwd_coil.cpp:125
Eigen::Matrix< float, Eigen::Dynamic, 3, Eigen::RowMajor > rmag
Definition fwd_coil.h:175
Eigen::VectorXf w
Definition fwd_coil.h:177
static FwdCoilSet::UPtr read_coil_defs(const QString &name)
bool is_axial_coil_type(int type) const
bool is_magnetometer_coil_type(int type) const
bool is_planar_coil_type(int type) const
FwdCoil::UPtr create_meg_coil(const FIFFLIB::FiffChInfo &ch, int acc, const FIFFLIB::FiffCoordTrans &t=FIFFLIB::FiffCoordTrans())
bool is_eeg_electrode_type(int type) const
FwdCoilSet::UPtr create_meg_coils(const QList< FIFFLIB::FiffChInfo > &chs, int nch, int acc, const FIFFLIB::FiffCoordTrans &t=FIFFLIB::FiffCoordTrans())
static FwdCoilSet::UPtr create_eeg_els(const QList< FIFFLIB::FiffChInfo > &chs, int nch, const FIFFLIB::FiffCoordTrans &t=FIFFLIB::FiffCoordTrans())
std::unique_ptr< FwdCoilSet > UPtr
std::vector< FwdCoil::UPtr > coils
FwdCoilSet::UPtr dup_coil_set(const FIFFLIB::FiffCoordTrans &t=FIFFLIB::FiffCoordTrans()) const
Eigen::MatrixX3f apply_trans(const Eigen::MatrixX3f &rr, bool do_move=true) const