# Converting Gradiometers to virtual magnetometers for Neuromag 122

**URL:** <https://mne.discourse.group/t/converting-gradiometers-to-virtual-magnetometers-for-neuromag-122/7056>\
**Category:** Support & Discussions\
**Tags:** meg, sensor-space, evoked\
**Created:** [June 9, 2023, 1:06pm UTC](https://mne.discourse.group/t/converting-gradiometers-to-virtual-magnetometers-for-neuromag-122/7056 "2023-06-09T13:06:54Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![dasdiptyajit](https://yyz2.discourse-cdn.com/free1/user_avatar/mne.discourse.group/dasdiptyajit/32/4796_2.png) [@dasdiptyajit](https://mne.discourse.group/u/dasdiptyajit)\
**Post date:** [June 9, 2023, 1:06pm UTC](https://mne.discourse.group/t/converting-gradiometers-to-virtual-magnetometers-for-neuromag-122/7056/1 "2023-06-09T13:06:54Z")

</div>

Hi all,

I am working on a script that allows to create virtual magnetometers from planar gradiometers for Neuromag 122 system. Currently, I am using MNE’s Vectorview magnetometers properties i.e., coil definitions to create the virtual magnetometers.

`Current implementation (testing phase): `  
[https://github.com/dasdiptyajit/meeg\_diprocessing/commit/4285d3fd5139e46dd58537dcf527c4783309b543](https://github.com/dasdiptyajit/meeg_diprocessing/commit/4285d3fd5139e46dd58537dcf527c4783309b543)

`Some results:`

 ![Screenshot from 2023-06-09 14-13-50](https://global.discourse-cdn.com/free1/uploads/mne/original/2X/8/80a3cacaa2debcc62f5465b161608e669ac55937.jpeg)  
 ![Screenshot from 2023-06-09 15-30-48](https://global.discourse-cdn.com/free1/uploads/mne/original/2X/b/b43371a58f5371614f7c983add4c7002f0cdbf11.png)

`Issue that I am facing now: `  
Interactive plot doesn’t work for combined evoked data (grad + mag). Its a layout issue. Reconstructed/virtual magnetometers are not present in the current channel layout, which is normal in this case. Is there a way to modify/update the channel layout??

I couldn’t manage to come up with an alternative solution, So it would be nice if someone can verify if the current implementation is legitimate.

PS: I am willing to share the Neuromag 122 data if you like to run the script for testing. Just let me know.

best,  
Dip

---

<div class="post-metadata">

**Author:** ![dasdiptyajit](https://yyz2.discourse-cdn.com/free1/user_avatar/mne.discourse.group/dasdiptyajit/32/4796_2.png) [@dasdiptyajit](https://mne.discourse.group/u/dasdiptyajit)\
**Post date:** [June 9, 2023, 1:08pm UTC](https://mne.discourse.group/t/converting-gradiometers-to-virtual-magnetometers-for-neuromag-122/7056/2 "2023-06-09T13:08:53Z")

</div>

@larsoner @mscheltienne @drammock I can use some help 🙂

---

<div class="post-metadata">

**Author:** ![larsoner](https://yyz2.discourse-cdn.com/free1/user_avatar/mne.discourse.group/larsoner/32/3_2.png) [@larsoner](https://mne.discourse.group/u/larsoner)\
**Post date:** [June 9, 2023, 6:37pm UTC](https://mne.discourse.group/t/converting-gradiometers-to-virtual-magnetometers-for-neuromag-122/7056/3 "2023-06-09T18:37:02Z")

</div>

You’re talking about layouts so I assume you mean for `plot_topo` (?). For the layout you can always create your own. In this case you’d have to create it for the grads, for the (virtual) mags, and then combine them somehow.

---

<div class="post-metadata">

**Author:** ![dasdiptyajit](https://yyz2.discourse-cdn.com/free1/user_avatar/mne.discourse.group/dasdiptyajit/32/4796_2.png) [@dasdiptyajit](https://mne.discourse.group/u/dasdiptyajit)\
**Post date:** [June 12, 2023, 4:13pm UTC](https://mne.discourse.group/t/converting-gradiometers-to-virtual-magnetometers-for-neuromag-122/7056/4 "2023-06-12T16:13:25Z")

</div>

Thanks for the reply. Yes, I am talking about the `plot_topo`.

I wrote a small script to create a custom layout for virtual magnetometers:

```python
info = evk_recon_mags.info
chs = info["chs"]
pos = np.empty((len(chs), 2))
for ci, ch in enumerate(chs):
    pos[ci] = ch["loc"][:2]

picks = mne.pick_channels(ch_names=info["ch_names"], include=[]).tolist()
vmaglout = mne.channels.generate_2d_layout(xy=pos, ch_names=info['ch_names'], ch_indices=picks,
                                           name='Neuromag_122_mag', normalize=True)
# replace original sensor position in the layout 
vmaglout.pos = np.array(ori_lout)
vmaglout.plot()

# read virtual layout
fname_lout = join(subjects_dir, 'Neuromag_122_mag.lout')
vmag_lout = mne.channels.read_layout(fname_lout)
evk_recon_mags.plot_topo(layout=vmag_lout)

```

 ![original_neuromag](https://global.discourse-cdn.com/free1/uploads/mne/original/2X/7/7d6ad33a9b5e5b28cf55cdda5dc0b2578a38bb05.png)  
 ![virtual_neuromag](https://global.discourse-cdn.com/free1/uploads/mne/original/2X/4/4df9e3623e7ddb8433f537222eecae28a651ecaf.png)

The new custom layout looks fine to me.

Is there any way to apply the custom layout to the data to replace the previous layout? I recon there is no implementation is currently available for MEG system.

I tried the `plot_topo` by passing the custom layout. But,  
`evk_recon_mags.plot_topo(layout=vmag_lout)` doesn’t work. Somehow MNE is not able read the channels info from the custom layout.

This is the `info` directory for my virtual magnetometers:

```python
 acq_pars: ACQch001 111001 ACQch002 111002 ACQch003 111003 ACQch004 111004 ...
 bads: []
 ch_names: MAG 001_v, MAG 003_v, MAG 005_v, MAG 007_v, MAG 009_v, MAG ...
 chs: 61 Magnetometers
 custom_ref_applied: False
 description: Anonymized using a time shift to preserve age at acquisition
 dev_head_t: MEG device -> head transform
 dig: 150 items (3 Cardinal, 4 HPI, 64 EEG, 79 Extra)
 events: 1 item (list)
 file_id: 4 items (dict)
 highpass: 0.5 Hz
 hpi_meas: 1 item (list)
 hpi_results: 1 item (list)
 lowpass: 30.0 Hz
 maxshield: False
 meas_date: 2000-01-01 00:00:00 UTC
 meas_id: 4 items (dict)
 nchan: 61
 proj_id: 1 item (ndarray)
 proj_name: mne_anonymize
 projs: []
 sfreq: 1000.0 Hz

```

Many thanks,  
Dip

---

<div class="post-metadata">

**Author:** ![dasdiptyajit](https://yyz2.discourse-cdn.com/free1/user_avatar/mne.discourse.group/dasdiptyajit/32/4796_2.png) [@dasdiptyajit](https://mne.discourse.group/u/dasdiptyajit)\
**Post date:** [June 12, 2023, 4:53pm UTC](https://mne.discourse.group/t/converting-gradiometers-to-virtual-magnetometers-for-neuromag-122/7056/5 "2023-06-12T16:53:00Z")

</div>

> [@dasdiptyajit](#):
>
> `evk_recon_mags.plot_topo(layout=vmag_lout)` doesn’t work. Somehow MNE is not able read the channels info from the custom layout.

I have slightly modified the `plot_topo` API, there is no error now. But, apparently sensors are not visible.

 ![Screenshot from 2023-06-12 18-33-06](https://global.discourse-cdn.com/free1/uploads/mne/original/2X/3/3a5309747b753ec3ee8863d7f7f6ed9e14fc5b84.png)

I have a feeling, this can be a BUG. When passing a custom layout, `plot_topo` doesn’t work. Can we test this somehow?

---

<div class="post-metadata">

**Author:** ![dasdiptyajit](https://yyz2.discourse-cdn.com/free1/user_avatar/mne.discourse.group/dasdiptyajit/32/4796_2.png) [@dasdiptyajit](https://mne.discourse.group/u/dasdiptyajit)\
**Post date:** [June 13, 2023, 1:47pm UTC](https://mne.discourse.group/t/converting-gradiometers-to-virtual-magnetometers-for-neuromag-122/7056/6 "2023-06-13T13:47:27Z")

</div>

I have found the issue. During creating the 2D custom layout, the API `mne.channels.generate_2d_layout` expects the channel names to be a continuous strings without a space. For example, if the channel name is `MAG 000` instead of `MAG000`; `plot_topo` doesn’t work (creates an empty figure).

This is the test code snippets! This can be run with MNE example data.

```python
"""
Test case: create custom layout and plot topoplot of evoked data
@author: diptyajit das <bmedasdiptyajit@gmail.com>
Created on june 13th, 2023
"""

# modules
from pathlib import Path
import os
import numpy as np
import mne

# Create some dummy magnetometers data
n_channels = 102
sampling_freq = 1000 # in Hertz
info = mne.create_info(n_channels, sfreq=sampling_freq)
ch_names = [f"MAG{n:03}" for n in range(n_channels)]
ch_types = ["mag"] * n_channels
info = mne.create_info(ch_names, ch_types=ch_types, sfreq=sampling_freq)
info["description"] = "custom data"
print('info directory', info)

# add some random data to Raw array
times = np.linspace(0, 1, sampling_freq, endpoint=False)
data = np.random.rand(n_channels, len(times)) * 1e-12 # mag channels scale
custom_raw = mne.io.RawArray(data, info)
custom_raw.plot(block=True)
lout_ori = mne.channels.find_layout(custom_raw.info)
print('default custom layout', lout_ori)

# create dummy evoked
epochs = mne.make_fixed_length_epochs(custom_raw, duration=.5, preload=True)
evoked = epochs.average()
evoked = evoked.filter(None, 40.)
evoked.plot()
print('channel locations', evoked.info['chs']) # at the moment, no locations for MEG channel is present

# define meg sample data path
sample_data_folder = mne.datasets.sample.data_path()
sample_data_raw_file = os.path.join(sample_data_folder, "MEG", "sample", "sample_audvis_raw.fif")
raw = mne.io.read_raw_fif(sample_data_raw_file)
raw.load_data()
raw = raw.copy().pick_types(meg='mag')

# add channel locations to custom evoked data from MNE sample data
for itype, ch in enumerate(evoked.info['ch_names']):
    pos_mag = evoked.info['ch_names'].index(ch)
    evoked.info['chs'][pos_mag]['loc'] = raw.info['chs'][pos_mag]['loc'] # add location

print('new channel locations', evoked.info['chs'])
evoked.plot()

# create a dummy custom layout, we will first extract xy coordinates from evoked data
info = evoked.info
chs = info["chs"]
pos = np.empty((len(chs), 2))
for ci, ch in enumerate(chs):
    pos[ci] = ch["loc"][:2]

picks = mne.pick_channels(ch_names=info["ch_names"], include=[]).tolist()

# modify channel names for layout
mod_ch_names1 = [f"MAG {n:03}_v" for n in range(n_channels)] # error occurs
mod_ch_names2 = [f"MAG {n:03}" for n in range(n_channels)] # error occurs

# test cases
types = [ch_names, mod_ch_names1, mod_ch_names2]

for type in types:
    dmaglout = mne.channels.generate_2d_layout(xy=pos, ch_names=type, ch_indices=picks,
                                               name='custom_mag_layout', normalize=True)

    # replace channel position with original layout ~ 2D custom layout doesn't match nicely with original layout,
    # so we will be strict to original one for now.
    # read the original layout
    layout_dir = Path(mne. __file__ ).parent / "channels" / "data" / "layouts"
    layouts = sorted(path.name for path in layout_dir.iterdir())
    print("\n" "BUILT-IN LAYOUTS\n" "================")
    print("\n".join(layouts))

    Vectorview_layout = mne.channels.read_layout("Vectorview-mag")
    dmaglout.pos = Vectorview_layout.pos[:n_channels]
    print('modified custom layout', dmaglout)
    dmaglout.plot()

    # now we will try to plot topomap for evoked with custom layout with changed channel names
    evoked.plot_topo(layout=dmaglout)

```

@larsoner Is it possible to add a warning massage or change the doc strings to recommend a specific naming for the channels?

---

<div class="post-metadata">

**Author:** ![larsoner](https://yyz2.discourse-cdn.com/free1/user_avatar/mne.discourse.group/larsoner/32/3_2.png) [@larsoner](https://mne.discourse.group/u/larsoner)\
**Post date:** [June 13, 2023, 3:13pm UTC](https://mne.discourse.group/t/converting-gradiometers-to-virtual-magnetometers-for-neuromag-122/7056/7 "2023-06-13T15:13:27Z")

</div>

Can you try to get that code trimmed to a minimal example (i.e., trim to as little code as possible so you can just show the bug – the example above has a lot more than needed I think) then open a GitHub issue? Hopefully in principle we can get `generate_2d_layout` to work with channel names with spaces, in principle it seems like it should work. But if we can’t get that to work then indeed a warning at least (an error might be better?) would be good.

The `sample` dataset already has spaces in the names so if the bug is as you describe then running `generate_2d_layout` on e.g. just the MEG channels should already show the bug
