Help with custom fNIRS montage

Hey all,
I try to get the correct sources and detector positions for my dataset into mne-nirs. I have 8 sources and 8 detectors and their positions correspond to 10-20 positions.
Here ist my mapping:

mapping = {
    'S1_D1 760': 'CP5_TP7 760',
    'S1_D2 760': 'CP5_C5 760',
    'S2_D1 760': 'T7_TP7 760',
    'S2_D2 760': 'T7_C5 760',
    'S2_D3 760': 'T7_FT7 760', 
    'S3_D2 760': 'FC5_C5 760',
    'S3_D3 760': 'FC5_FT7 760',
    'S3_D4 760': 'FC5_F5 760',
    'S4_D3 760': 'F7_FT7 760',
    'S4_D4 760': 'F7_F5 760',
    'S5_D5 760': 'F8_F6 760',
    'S5_D6 760': 'F8_FT8 760',
    'S6_D5 760': 'FC6_F6 760',
    'S6_D6 760': 'FC6_FT8 760',
    'S6_D7 760': 'FC6_C6 760',
    'S7_D6 760': 'T8_FT8 760',
    'S7_D7 760': 'T8_C6 760',
    'S7_D8 760': 'T8_TP8 760',
    'S8_D7 760': 'CP6_C6 760',
    'S8_D8 760': 'CP6_TP8 760',
    'S1_D1 850': 'CP5_TP7 850',
    'S1_D2 850': 'CP5_C5 850',
    'S2_D1 850': 'T7_TP7 850',
    'S2_D2 850': 'T7_C5 850',
    'S2_D3 850': 'T7_FT7 850', 
    'S3_D2 850': 'FC5_C5 850',
    'S3_D3 850': 'FC5_FT7 850',
    'S3_D4 850': 'FC5_F5 850',
    'S4_D3 850': 'F7_FT7 850',
    'S4_D4 850': 'F7_F5 850',
    'S5_D5 850': 'F8_F6 850',
    'S5_D6 850': 'F8_FT8 850',
    'S6_D5 850': 'FC6_F6 850',
    'S6_D6 850': 'FC6_FT8 850',
    'S6_D7 850': 'FC6_C6 850',
    'S7_D6 850': 'T8_FT8 850',
    'S7_D7 850': 'T8_C6 850',
    'S7_D8 850': 'T8_TP8 850',
    'S8_D7 850': 'CP6_C6 850',
    'S8_D8 850': 'CP6_TP8 850'
}

My question is, how can I get the correct 10-20 positions imported for the source-detector pairs? I already tried to rename the channels according to my mapping and set the standard_1020 montage afterwards but I get the following error for raw_intensity.set_montage("standard_1020"):

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[74], line 2
      1 mne.rename_channels(raw_intensity.info, mapping)
----> 2 raw_intensity.set_montage("standard_1020")

File <decorator-gen-21>:12, in set_montage(self, montage, match_case, match_alias, on_missing, verbose)

File ~\AppData\Roaming\Python\Python312\site-packages\mne\_fiff\meas_info.py:427, in MontageMixin.set_montage(self, montage, match_case, match_alias, on_missing, verbose)
    424 from ..channels.montage import _set_montage
    426 info = self if isinstance(self, Info) else self.info
--> 427 _set_montage(info, montage, match_case, match_alias, on_missing)
    428 return self

File ~\AppData\Roaming\Python\Python312\site-packages\mne\channels\montage.py:1337, in _set_montage(***failed resolving arguments***)
   1335 fnirs_picks = _picks_to_idx(info, "fnirs", allow_empty=True)
   1336 if len(fnirs_picks) > 0:
-> 1337     _set_montage_fnirs(info, mnt_head)

File ~\AppData\Roaming\Python\Python312\site-packages\mne\channels\montage.py:1089, in _set_montage_fnirs(info, montage)
   1086 from ..preprocessing.nirs import _validate_nirs_info
   1088 # Validate that the fNIRS info is correctly formatted
-> 1089 picks = _validate_nirs_info(info)
   1091 # Modify info['chs'][#]['loc'] in place
   1092 num_ficiduals = len(montage.dig) - len(montage.ch_names)

File ~\AppData\Roaming\Python\Python312\site-packages\mne\preprocessing\nirs\nirs.py:264, in _validate_nirs_info(info, throw_errors, fnirs, which, check_bads, allow_empty)
    260     if not len(pick_types(info, fnirs=fnirs)):
    261         raise RuntimeError(
    262             f"{which} must operate on {kind} data, but none was found."
    263         )
--> 264 freqs = np.unique(_channel_frequencies(info))
    265 if freqs.size > 0:
    266     pair_vals = freqs

File ~\AppData\Roaming\Python\Python312\site-packages\mne\preprocessing\nirs\nirs.py:76, in _channel_frequencies(info)
     74 freqs = list()
     75 for pick in picks:
---> 76     freqs.append(round(float(_S_D_F_RE.match(info["ch_names"][pick]).groups()[2])))
     77 return np.array(freqs, int)

AttributeError: 'NoneType' object has no attribute 'groups'

Thank you for any help!

Solved it by creating my own montage file based on standard_1020.elc and loading it with mne.channels.read_custom_montage

2 Likes