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