diff --git a/kwant/builder.py b/kwant/builder.py
index e8a5f34c5deb1df3db2b167611098e31d200170a..ca6729e29cffa33fed6a32a87783e051e89f9b8b 100644
--- a/kwant/builder.py
+++ b/kwant/builder.py
@@ -830,7 +830,8 @@ class Builder:
     # Each edge, specified by a ``(tail, head)`` pair of nodes, holds an object
     # as a value.  Likewise, each tail which occurs in the graph also holds a
     # value.  (Nodes which only occur as heads are not required to have
-    # values.)
+    # values.) Every tail node has to be in the fundamental domain of the
+    # builder's symmetry.
     #
     # For a given `tail` site, H[tail] is a list alternately storing
     # heads and values.  (The heads occupy even locations followed by the
diff --git a/kwant/tests/test_wraparound.py b/kwant/tests/test_wraparound.py
index c1233a97cb71aef6e31b240bac616081b6089f0d..18585275895f7d4c34f91c86868139ed3a1dc505 100644
--- a/kwant/tests/test_wraparound.py
+++ b/kwant/tests/test_wraparound.py
@@ -300,10 +300,9 @@ def test_fd_mismatch():
         else:
             def _(*args):
                 args = list(args)
-                assert len(args) == 2
                 kext = args.pop(keep)
-                kint = args[0]
-                B = kwant.physics.Bands(syst, args=(kint,))
+                kint = args
+                B = kwant.physics.Bands(syst, args=kint)
                 return B(kext)
         return _
 
@@ -312,3 +311,82 @@ def test_fd_mismatch():
     # is more tricky, as we would also need to transform the k vectors
     E_k = np.array([spec(0, 0) for spec in spectra]).transpose()
     assert all(np.allclose(E, E[0]) for E in E_k)
+
+    # Test square lattice with oblique unit cell
+    lat = kwant.lattice.general(np.eye(2))
+    translations = kwant.lattice.TranslationalSymmetry([2, 2], [0, 2])
+    syst = kwant.Builder(symmetry=translations)
+    syst[lat.shape(lambda site: True, [0, 0])] = 1
+    syst[lat.neighbors()] = 1
+    # Check that spectra are the same at k=0.
+    spectra = [spectrum(syst, keep) for keep in (None, 0, 1)]
+    E_k = np.array([spec(0, 0) for spec in spectra]).transpose()
+    assert all(np.allclose(E, E[0]) for E in E_k)
+
+    # Test Rocksalt structure
+    # cubic lattice that contains both sublattices
+    lat = kwant.lattice.general(np.eye(3))
+    # Builder with FCC translational symmetries.
+    translations = kwant.lattice.TranslationalSymmetry([1, 1, 0], [1, 0, 1], [0, 1, 1])
+    syst = kwant.Builder(symmetry=translations)
+    syst[lat(0, 0, 0)] = 1
+    syst[lat(0, 0, 1)] = -1
+    syst[lat.neighbors()] = 1
+    # Check that spectra are the same at k=0.
+    spectra = [spectrum(syst, keep) for keep in (None, 0, 1, 2)]
+    E_k = np.array([spec(0, 0, 0) for spec in spectra]).transpose()
+    assert all(np.allclose(E, E[0]) for E in E_k)
+    # Same with different translation vectors
+    translations = kwant.lattice.TranslationalSymmetry([1, 1, 0], [1, -1, 0], [0, 1, 1])
+    syst = kwant.Builder(symmetry=translations)
+    syst[lat(0, 0, 0)] = 1
+    syst[lat(0, 0, 1)] = -1
+    syst[lat.neighbors()] = 1
+    # Check that spectra are the same at k=0.
+    spectra = [spectrum(syst, keep) for keep in (None, 0, 1, 2)]
+    E_k = np.array([spec(0, 0, 0) for spec in spectra]).transpose()
+    assert all(np.allclose(E, E[0]) for E in E_k)
+
+    # Test that spectrum in slab geometry is identical regardless of choice of unit
+    # cell in rocksalt structure
+    def shape(site):
+        return abs(site.tag[2]) < 4
+
+    lat = kwant.lattice.general(np.eye(3))
+    # First choice: primitive UC
+    translations = kwant.lattice.TranslationalSymmetry([1, 1, 0], [1, -1, 0], [1, 0, 1])
+    syst = kwant.Builder(symmetry=translations)
+    syst[lat(0, 0, 0)] = 1
+    syst[lat(0, 0, 1)] = -1
+    # Set all the nearest neighbor hoppings
+    for d in [[1, 0, 0], [-1, 0, 0], [0, 1, 0], [0, -1, 0], [0, 0, 1], [0, 0, -1]]:
+        syst[(lat(0, 0, 0), lat(*d))] = 1
+
+    wrapped = kwant.wraparound.wraparound(syst, keep=2)
+    finitewrapped = kwant.Builder()
+    finitewrapped.fill(wrapped, shape, start=np.zeros(3));
+
+    sysf = finitewrapped.finalized()
+    spectrum1 = [np.linalg.eigvalsh(sysf.hamiltonian_submatrix(args=(k, 0)))
+                for k in np.linspace(-np.pi, np.pi, 5)]
+
+    # Second choice: doubled UC with third translation purely in z direction
+    translations = kwant.lattice.TranslationalSymmetry([1, 1, 0], [1, -1, 0], [0, 0, 2])
+    syst = kwant.Builder(symmetry=translations)
+    syst[lat(0, 0, 0)] = 1
+    syst[lat(0, 0, 1)] = -1
+    syst[lat(1, 0, 1)] = 1
+    syst[lat(-1, 0, 0)] = -1
+    for s in np.array([[0, 0, 0], [1, 0, 1]]):
+        for d in np.array([[1, 0, 0], [-1, 0, 0], [0, 1, 0], [0, -1, 0], [0, 0, 1], [0, 0, -1]]):
+            syst[(lat(*s), lat(*(s + d)))] = 1
+    wrapped = kwant.wraparound.wraparound(syst, keep=2)
+    finitewrapped = kwant.Builder()
+
+    finitewrapped.fill(wrapped, shape, start=np.zeros(3));
+
+    sysf = finitewrapped.finalized()
+    spectrum2 = [np.linalg.eigvalsh(sysf.hamiltonian_submatrix(args=(k, 0)))
+                for k in np.linspace(-np.pi, np.pi, 5)]
+
+    assert np.allclose(spectrum1, spectrum2)
diff --git a/kwant/wraparound.py b/kwant/wraparound.py
index 2e1cb1dd1b63b2f7cd051a9687616724b81a6759..d43d6cc8f343709d97f57cf417a8be72b80afd8a 100644
--- a/kwant/wraparound.py
+++ b/kwant/wraparound.py
@@ -269,22 +269,29 @@ def wraparound(builder, keep=None, *, coordinate_names=('x', 'y', 'z')):
     mnp = -len(sym.periods)      # Used by the bound functions above.
 
     # Store lists of values, so that multiple values can be assigned to the
-    # same site or hopping. We map to the FD of 'sym', as the hopping-processing
-    # code assumes the sites are in the FD.
+    # same site or hopping.
     for site, val in builder.site_value_pairs():
-        sites[sym.to_fd(site)] = [bind_site(val) if callable(val) else val]
+        # Every 'site' is in the FD of the original symmetry.
+        # Move the sites to the FD of the remaining symmetry, this guarantees that
+        # every site in the new system is an image of an original FD site translated
+        # purely by the remaining symmetry.
+        sites[ret.symmetry.to_fd(site)] = [bind_site(val) if callable(val) else val]
 
     for hop, val in builder.hopping_value_pairs():
-        # Map hopping to FD of 'sym', as the code afterwards assumes that 'a'
-        # is in this domain.
-        a, b = sym.to_fd(*hop)
-        b_dom = sym.which(b)
+        a, b = hop
+        # 'a' is in the FD of original symmetry.
+        # Get translation from FD of original symmetry to 'b',
+        # this is different from 'b_dom = sym.which(b)'.
+        b_dom = builder.symmetry.which(b)
+        # Throw away part that is in the remaining translation direction, so we get
+        # an element of 'sym' which is being wrapped
+        b_dom = ta.array([t for i, t in enumerate(b_dom) if i != keep])
+        # Pull back using the remainder, which is purely in the wrapped directions.
+        # This guarantees that 'b_wa' is an image of an original FD site translated
+        # purely by the remaining symmetry.
         b_wa = sym.act(-b_dom, b)
-        # Now map 'b_wa' to another domain of 'sym', so that the hopping
-        # is compatible with 'ret.symmetry'. This is necessary when the
-        # fundamental domains of the symmetries do not coincide.
-        w = ret.symmetry.which(b_wa)
-        b_wa = ret.symmetry.act(w, sym.to_fd(ret.symmetry.act(-w, b_wa)))
+        # Move the hopping to the FD of the remaining symmetry
+        a, b_wa = ret.symmetry.to_fd(a, b_wa)
 
         if a == b_wa:
             # The hopping gets wrapped-around into an onsite Hamiltonian.
@@ -292,13 +299,14 @@ def wraparound(builder, keep=None, *, coordinate_names=('x', 'y', 'z')):
             sites[a].append(bind_hopping_as_site(b_dom, val))
         else:
             # The hopping remains a hopping.
-            if b != b_wa or callable(val):
+            if any(b_dom) or callable(val):
                 # The hopping got wrapped-around or is a function.
                 val = bind_hopping(b_dom, val)
 
             # Make sure that there is only one entry for each hopping
-            # (pointing in one direction).
-            if (b_wa, a) in hops:
+            # pointing in one direction, modulo the remaining translations.
+            b_wa_r, a_r = ret.symmetry.to_fd(b_wa, a)
+            if (b_wa_r, a_r) in hops:
                 assert (a, b_wa) not in hops
                 if callable(val):
                     assert not isinstance(val, HermConjOfFunc)
@@ -306,7 +314,7 @@ def wraparound(builder, keep=None, *, coordinate_names=('x', 'y', 'z')):
                 else:
                     val = herm_conj(val)
 
-                hops[b_wa, a].append(val)
+                hops[b_wa_r, a_r].append(val)
             else:
                 hops[a, b_wa].append(val)