v2.0.0
Loading...
Searching...
No Matches
fwd_coil_set.cpp
Go to the documentation of this file.
1//=============================================================================================================
15
16//=============================================================================================================
17// INCLUDES
18//=============================================================================================================
19
20#include "fwd_coil_set.h"
21#include "fwd_coil.h"
22#include "fwd_bem_solution.h"
23
24#include <fiff/fiff_ch_info.h>
25
26//=============================================================================================================
27// QT INCLUDES
28//=============================================================================================================
29
30#include <QDebug>
31#include <QFile>
32#include <QLocale>
33#include <QTextStream>
34
35//=============================================================================================================
36// USED NAMESPACES
37//=============================================================================================================
38
39using namespace Eigen;
40using namespace FIFFLIB;
41using namespace FWDLIB;
42
43namespace
44{
45constexpr float BIG = 0.5f;
46}
47
51static QString readWord(QTextStream& in)
52{
53 in.skipWhiteSpace();
54 if (in.atEnd())
55 return QString();
56
57 QChar ch;
58 in >> ch;
59
60 if (ch == '"') {
61 QString word;
62 while (!in.atEnd()) {
63 in >> ch;
64 if (ch == '"')
65 break;
66 word += ch;
67 }
68 return word;
69 }
70
71 QString word(ch);
72 while (!in.atEnd()) {
73 in >> ch;
74 if (ch.isSpace())
75 break;
76 word += ch;
77 }
78 return word;
79}
80
81FwdCoil* FwdCoilSet::fwd_add_coil_to_set(int type, int coil_class, int acc, int np, float size, float base, const QString& desc)
82{
83 if (np <= 0) {
84 qWarning("Number of integration points should be positive (type = %d acc = %d)", type, acc);
85 return nullptr;
86 }
87 if (!(acc == FWD_COIL_ACCURACY_POINT ||
90 qWarning("Illegal accuracy (type = %d acc = %d)", type, acc);
91 return nullptr;
92 }
93 if (!(coil_class == FWD_COILC_MAG ||
94 coil_class == FWD_COILC_AXIAL_GRAD ||
95 coil_class == FWD_COILC_PLANAR_GRAD ||
96 coil_class == FWD_COILC_AXIAL_GRAD2)) {
97 qWarning("Illegal coil class (type = %d acc = %d class = %d)", type, acc, coil_class);
98 return nullptr;
99 }
100
101 coils.push_back(std::make_unique<FwdCoil>(np));
102 FwdCoil* def = coils.back().get();
103
104 def->type = type;
105 def->coil_class = coil_class;
106 def->accuracy = acc;
107 def->np = np;
108 def->size = size;
109 def->base = base;
110 if (!desc.isEmpty())
111 def->desc = desc;
112 return def;
113}
114
115//=============================================================================================================
116// DEFINE MEMBER METHODS
117//=============================================================================================================
118
123
124//=============================================================================================================
125
129
130//=============================================================================================================
131
133{
134 if (ch.kind != FIFFV_MEG_CH && ch.kind != FIFFV_REF_MEG_CH) {
135 qWarning() << ch.ch_name << "is not a MEG channel. Cannot create a coil definition.";
136 return nullptr;
137 }
138 /*
139 * Simple linear search from the coil definitions
140 */
141 FwdCoil* def = nullptr;
142 for (int k = 0; k < this->ncoil(); k++) {
143 if ((this->coils[k]->type == (ch.chpos.coil_type & 0xFFFF)) &&
144 this->coils[k]->accuracy == acc) {
145 def = this->coils[k].get();
146 }
147 }
148 if (!def) {
149 qWarning("Desired coil definition not found (type = %d acc = %d)", ch.chpos.coil_type, acc);
150 return nullptr;
151 }
152 /*
153 * Create the result
154 */
155 auto res = std::make_unique<FwdCoil>(def->np);
156
157 res->chname = ch.ch_name;
158 if (!def->desc.isEmpty())
159 res->desc = def->desc;
160 res->coil_class = def->coil_class;
161 res->accuracy = def->accuracy;
162 res->base = def->base;
163 res->size = def->size;
164 res->type = ch.chpos.coil_type;
165
166 res->r0 = ch.chpos.r0;
167 res->ex = ch.chpos.ex;
168 res->ey = ch.chpos.ey;
169 res->ez = ch.chpos.ez;
170 /*
171 * Apply a coordinate transformation if so desired
172 */
173 if (!t.isEmpty()) {
174 FiffCoordTrans::apply_trans(res->r0.data(), t, FIFFV_MOVE);
175 FiffCoordTrans::apply_trans(res->ex.data(), t, FIFFV_NO_MOVE);
176 FiffCoordTrans::apply_trans(res->ey.data(), t, FIFFV_NO_MOVE);
177 FiffCoordTrans::apply_trans(res->ez.data(), t, FIFFV_NO_MOVE);
178 res->coord_frame = t.to;
179 } else
180 res->coord_frame = FIFFV_COORD_DEVICE;
181
182 for (int p = 0; p < res->np; p++) {
183 res->w[p] = def->w[p];
184 res->rmag.row(p) = (res->r0 + def->rmag(p, 0) * res->ex + def->rmag(p, 1) * res->ey + def->rmag(p, 2) * res->ez).transpose();
185 res->cosmag.row(p) = (def->cosmag(p, 0) * res->ex + def->cosmag(p, 1) * res->ey + def->cosmag(p, 2) * res->ez).transpose();
186 }
187 return res;
188}
189
190//=============================================================================================================
191
192FwdCoilSet::UPtr FwdCoilSet::create_meg_coils(const QList<FIFFLIB::FiffChInfo>& chs,
193 int nch,
194 int acc,
195 const FiffCoordTrans& t)
196{
197 auto res = std::make_unique<FwdCoilSet>();
198
199 for (int k = 0; k < nch; k++) {
200 auto next = this->create_meg_coil(chs.at(k), acc, t);
201 if (!next)
202 return nullptr;
203 res->coils.push_back(std::move(next));
204 }
205 if (!t.isEmpty())
206 res->coord_frame = t.to;
207 return res;
208}
209
210//=============================================================================================================
211
212FwdCoilSet::UPtr FwdCoilSet::create_eeg_els(const QList<FIFFLIB::FiffChInfo>& chs,
213 int nch,
214 const FiffCoordTrans& t)
215{
216 auto res = std::make_unique<FwdCoilSet>();
217
218 for (int k = 0; k < nch; k++) {
219 auto next = FwdCoil::create_eeg_el(chs.at(k), t);
220 if (!next)
221 return nullptr;
222 res->coils.push_back(std::move(next));
223 }
224 if (!t.isEmpty())
225 res->coord_frame = t.to;
226 return res;
227}
228
229//=============================================================================================================
230
232{
233 QFile file(name);
234 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
235 qWarning() << "FwdCoilSet::read_coil_defs - Cannot open" << name;
236 return nullptr;
237 }
238
239 // Read file content, stripping comments
240 QString content;
241 {
242 QTextStream fileIn(&file);
243 while (!fileIn.atEnd()) {
244 QString line = fileIn.readLine();
245 int idx = line.indexOf('#');
246 if (idx >= 0)
247 line.truncate(idx);
248 content += line + '\n';
249 }
250 }
251 file.close();
252
253 QTextStream in(&content);
254 in.setLocale(QLocale::c());
255
256 auto res = std::make_unique<FwdCoilSet>();
257 while (!in.atEnd()) {
258 /*
259 * Read basic info
260 */
261 int coil_class;
262 in >> coil_class;
263 if (in.status() != QTextStream::Ok)
264 break;
265
266 int type, acc, np;
267 float size, base;
268
269 in >> type >> acc >> np >> size >> base;
270 if (in.status() != QTextStream::Ok) {
271 qWarning("FwdCoilSet::read_coil_defs - Error reading coil header");
272 return nullptr;
273 }
274
275 QString desc = readWord(in);
276 if (desc.isEmpty()) {
277 qWarning("FwdCoilSet::read_coil_defs - Missing coil description");
278 return nullptr;
279 }
280
281 FwdCoil* def = res->fwd_add_coil_to_set(type, coil_class, acc, np, size, base, desc);
282 if (!def)
283 return nullptr;
284
285 for (int p = 0; p < def->np; p++) {
286 /*
287 * Read and verify data for each integration point
288 */
289 in >> def->w[p] >> def->rmag(p, 0) >> def->rmag(p, 1) >> def->rmag(p, 2) >> def->cosmag(p, 0) >> def->cosmag(p, 1) >> def->cosmag(p, 2);
290 if (in.status() != QTextStream::Ok) {
291 qWarning("FwdCoilSet::read_coil_defs - Error reading integration point %d", p);
292 return nullptr;
293 }
294
295 if (def->pos(p).norm() > BIG) {
296 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);
297 return nullptr;
298 }
299 float cosmagNorm = def->dir(p).norm();
300 if (cosmagNorm <= 0) {
301 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);
302 return nullptr;
303 }
304 def->cosmag.row(p).normalize();
305 }
306 }
307
308 qInfo("%d coil definitions read", res->ncoil());
309 return res;
310}
311
312//=============================================================================================================
313
315{
317
318 if (!t.isEmpty()) {
319 if (this->coord_frame != t.from) {
320 qWarning("Coordinate frame of the transformation does not match the coil set in fwd_dup_coil_set");
321 return nullptr;
322 }
323 }
324 res = std::make_unique<FwdCoilSet>();
325 if (!t.isEmpty())
326 res->coord_frame = t.to;
327 else
328 res->coord_frame = this->coord_frame;
329
330 res->coils.reserve(this->ncoil());
331
332 for (int k = 0; k < this->ncoil(); k++) {
333 auto coil = std::make_unique<FwdCoil>(*(this->coils[k]));
334 /*
335 * Optional coordinate transformation
336 */
337 if (!t.isEmpty()) {
338 FiffCoordTrans::apply_trans(coil->r0.data(), t, FIFFV_MOVE);
339 FiffCoordTrans::apply_trans(coil->ex.data(), t, FIFFV_NO_MOVE);
340 FiffCoordTrans::apply_trans(coil->ey.data(), t, FIFFV_NO_MOVE);
341 FiffCoordTrans::apply_trans(coil->ez.data(), t, FIFFV_NO_MOVE);
342
343 for (int p = 0; p < coil->np; p++) {
344 FiffCoordTrans::apply_trans(&coil->rmag(p, 0), t, FIFFV_MOVE);
345 FiffCoordTrans::apply_trans(&coil->cosmag(p, 0), t, FIFFV_NO_MOVE);
346 }
347 coil->coord_frame = t.to;
348 }
349 res->coils.push_back(std::move(coil));
350 }
351 return res;
352}
353
354//=============================================================================================================
355
357{
358 if (type == FIFFV_COIL_EEG)
359 return false;
360 for (int k = 0; k < this->ncoil(); k++)
361 if (this->coils[k]->type == type)
362 return this->coils[k]->coil_class == FWD_COILC_PLANAR_GRAD;
363 return false;
364}
365
366//=============================================================================================================
367
369{
370 if (type == FIFFV_COIL_EEG)
371 return false;
372 for (int k = 0; k < this->ncoil(); k++)
373 if (this->coils[k]->type == type)
374 return (this->coils[k]->coil_class == FWD_COILC_MAG ||
375 this->coils[k]->coil_class == FWD_COILC_AXIAL_GRAD ||
376 this->coils[k]->coil_class == FWD_COILC_AXIAL_GRAD2);
377 return false;
378}
379
380//=============================================================================================================
381
383{
384 if (type == FIFFV_COIL_EEG)
385 return false;
386 for (int k = 0; k < this->ncoil(); k++)
387 if (this->coils[k]->type == type)
388 return this->coils[k]->coil_class == FWD_COILC_MAG;
389 return false;
390}
391
392//=============================================================================================================
393
395{
396 return type == FIFFV_COIL_EEG;
397}
FIFF channel descriptor record (FIFF_CH_INFO): per-channel logical/scanner numbers,...
#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
Container of FwdCoil instances representing either a sensor-type template database or a concrete per-...
Single MEG sensor coil or EEG electrode described by a set of weighted integration points in its own ...
Per-sensor projection matrix that turns BEM node potentials into MEG coil readings or EEG electrode v...
constexpr int BIG
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_POINT
Definition fwd_coil.h:75
constexpr int FWD_COIL_ACCURACY_ACCURATE
Definition fwd_coil.h:77
constexpr int FWD_COILC_PLANAR_GRAD
Definition fwd_coil.h:72
constexpr int FWD_COILC_AXIAL_GRAD2
Definition fwd_coil.h:73
constexpr int FWD_COILC_AXIAL_GRAD
Definition fwd_coil.h:71
constexpr int FWD_COILC_MAG
Definition fwd_coil.h:70
Per-channel FIFF descriptor: identifiers, kind, calibration, coil type, channel-frame coil position a...
Eigen::Vector3f r0
fiff_int_t coil_type
Eigen::Vector3f ey
Eigen::Vector3f ex
Eigen::Vector3f ez
Labelled 4x4 FIFF affine: source frame, destination frame, rotation, translation and cached inverse.
Eigen::MatrixX3f apply_trans(const Eigen::MatrixX3f &rr, bool do_move=true) const
Single MEG sensor coil or EEG electrode — stores the coil-local frame and the (r_mag,...
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:197
Eigen::Map< const Eigen::Vector3f > pos(int j) const
Definition fwd_coil.h:187
Eigen::Matrix< float, Eigen::Dynamic, 3, Eigen::RowMajor > cosmag
Definition fwd_coil.h:178
static FwdCoil::UPtr create_eeg_el(const FIFFLIB::FiffChInfo &ch, const FIFFLIB::FiffCoordTrans &t=FIFFLIB::FiffCoordTrans())
Definition fwd_coil.cpp:102
Eigen::Matrix< float, Eigen::Dynamic, 3, Eigen::RowMajor > rmag
Definition fwd_coil.h:177
Eigen::VectorXf w
Definition fwd_coil.h:179
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