Repository: mne-tools/mne-python

Issue:
Adding a reference does not work after a montage has been set
The order in which you perform assigning a montage and adding a reference matters, which probably shouldn't be the case.

Consider the following example using the testing data from `mne/io/brainvision/tests/data/test.*` (the first two lines could be combined by supplying the `montage` argument in the call to `mne.io.read_raw_brainvision`):

```
raw = mne.io.read_raw_brainvision("test.vhdr", preload=True)
raw.set_montage("standard_1020")
raw = mne.io.add_reference_channels(raw, "T3")
```

This assigns the montage first and then adds a new reference channel which wasn't part of the data. This produces a `RuntimeWarning: The locations of multiple reference channels are ignored (set to zero).`, which results in an incorrect location of the newly added channel T3.

If instead you first add the reference channel and then set the montage, everything works as expected:

 ```
raw = mne.io.read_raw_brainvision("test.vhdr", preload=True)
raw = mne.io.add_reference_channels(raw, "T3")
raw.set_montage("standard_1020")
```


Discussion:
indeed. Do you have time to fix this?

The fix isn't obvious based on how applying montages currently works. One option is to add all montage entries to `info['dig']`, which isn't currently done. Then when adding a channel, check to see if it's in the list of digitized locations.

I'd probably just wait to tackle this once digitization/montages are properly refactored.
I didn't know that `info['dig']` is needed in the referencing process, why is this necessary? I'm +1 on tackling this properly instead of a quick fix - is anyone working on this?
It's not needed for reference calculation. I assumed the problem you mention had to do with the location in space not being set. The error message might be misleading on this point, but I suspect it's the underlying problem.

Also I realized dig does not say which channel each point is associated with so the fix I proposed won't work. I can't think of a clean solution. We could hack something like storing the unused channel names and locations in case you add one later, but this is ugly and will not survive IO roundtrip.

I'm in inclined to say the solution is in the user side. Either use the other order with no warning, or live with the warning and reapply the montage afterward to get the location added. In other words, the rule is that applying a montage will only set locations of channels that exist at the time it is applied. And thus the fix here is just a documentation update to say that.

And nobody is actively working on the digitization refactoring yet. I hope to get to it for 0.18.
I agree that updating the documentation is the way to go here. I think a clear warning message would mention that you cannot add a new channel because a montage has already been set, and therefore the montage needs to be re-applied.

Alternatively, what about this hack: if the user adds a channel after a montage has been set, we could automatically re-apply the montage if we knew the montage name - is this stored anywhere? We could then only issue the warning if a channel that is not part of the montage was added (then of course we need to set its location to zero).
I prefer the clear doc + warning approach than the more magical one.

>  is anyone working on this?

> And nobody is actively working on the digitization refactoring yet. I hope to get to it for 0.18.

I don't mind teaming up with someone to approach this after we release. It would bring me light to a part of the io that is still shady to me.
@massich that would be awesome. Feel free to read the discussion in #3893, it contains the proposal.
FWIW, #6369 didn't solve this issue (maybe it wasn't supposed to, I didn't really follow this topic closely).
sure. #6369  aims to simplify the future changes by putting everything in one place. No real API change has been done yet.
I guess I should make an issue with the notes of the plan we discussed with @larsoner  so that is easier to follow the status for everyone. Right now I only have messy notes.

Resolving patch:
diff --git a/mne/_fiff/meas_info.py b/mne/_fiff/meas_info.py
--- a/mne/_fiff/meas_info.py
+++ b/mne/_fiff/meas_info.py
@@ -410,6 +410,12 @@ def set_montage(
             a montage. Other channel types (e.g., MEG channels) should have
             their positions defined properly using their data reading
             functions.
+        .. warning::
+            Applying a montage will only set locations of channels that exist
+            at the time it is applied. This means when
+            :ref:`re-referencing <tut-set-eeg-ref>`
+            make sure to apply the montage only after calling
+            :func:`mne.add_reference_channels`
         """
         # How to set up a montage to old named fif file (walk through example)
         # https://gist.github.com/massich/f6a9f4799f1fbeb8f5e8f8bc7b07d3df
diff --git a/mne/_fiff/reference.py b/mne/_fiff/reference.py
--- a/mne/_fiff/reference.py
+++ b/mne/_fiff/reference.py
@@ -175,6 +175,14 @@ def add_reference_channels(inst, ref_channels, copy=True):
     -------
     inst : instance of Raw | Epochs | Evoked
         Data with added EEG reference channels.
+
+    Notes
+    -----
+    .. warning::
+        When :ref:`re-referencing <tut-set-eeg-ref>`,
+        make sure to apply the montage using :meth:`mne.io.Raw.set_montage`
+        only after calling this function. Applying a montage will only set
+        locations of channels that exist at the time it is applied.
     """
     from ..epochs import BaseEpochs
     from ..evoked import Evoked
@@ -239,7 +247,9 @@ def add_reference_channels(inst, ref_channels, copy=True):
         ref_dig_array = np.full(12, np.nan)
         logger.info(
             "Location for this channel is unknown; consider calling "
-            "set_montage() again if needed."
+            "set_montage() after adding new reference channels if needed. "
+            "Applying a montage will only set locations of channels that "
+            "exist at the time it is applied."
         )
 
     for ch in ref_channels:


Test patch: