diff --git a/dumux/mixeddimension/assembler.hh b/dumux/mixeddimension/assembler.hh
index 8c25ab1cbfc203d5a73ca6a5a004afb26e08cc04..b66efd7f4fb983ef36a1fff23473d4ea02aaaeff 100644
--- a/dumux/mixeddimension/assembler.hh
+++ b/dumux/mixeddimension/assembler.hh
@@ -182,7 +182,8 @@ protected:
                                                               residual_[lowDimIdx]);
     }
 
-    void buildBulkMatrixBlocksCC_(BulkMatrixBlock& m, BulkCouplingMatrixBlock& cm)
+    template <class T = BulkProblemTypeTag, typename std::enable_if<((!GET_PROP_VALUE(T, ImplicitIsBox))), bool>::type = 0>
+    void buildBulkMatrixBlocks_(BulkMatrixBlock& m, BulkCouplingMatrixBlock& cm)
     {
         // get occupation pattern of the matrix
         Dune::MatrixIndexSet bulkPattern, bulkCouplingPattern;
@@ -213,7 +214,8 @@ protected:
         bulkCouplingPattern.exportIdx(cm);
     }
 
-    void buildLowDimMatrixBlocksCC_(LowDimMatrixBlock& m, LowDimCouplingMatrixBlock& cm)
+    template <class T = LowDimProblemTypeTag, typename std::enable_if<((!GET_PROP_VALUE(T, ImplicitIsBox))), bool>::type = 0>
+    void buildLowDimMatrixBlocks_(LowDimMatrixBlock& m, LowDimCouplingMatrixBlock& cm)
     {
         // get occupation pattern of the matrix
         Dune::MatrixIndexSet lowDimPattern, lowDimCouplingPattern;
@@ -244,63 +246,73 @@ protected:
         lowDimCouplingPattern.exportIdx(cm);
     }
 
-    // void buildBulkMatrixBlocksBox_(BulkMatrixBlock& m, BulkCouplingMatrixBlock& cm)
-    // {
-    //     // get occupation pattern of the matrix
-    //     Dune::MatrixIndexSet bulkPattern, bulkCouplingPattern;
-    //     bulkPattern.resize(m.N(), m.M());
-    //     bulkCouplingPattern.resize(cm.N(), cm.M());
-
-    //     for (const auto& element : elements(problem_().bulkGridView()))
-    //     {
-    //         const auto& stencil = problem_().couplingManager().stencil(element);
-    //         const auto& couplingStencil = problem_().couplingManager().couplingStencil(element);
-
-    //         for (unsigned int scvIdx = 0; scvIdx < element.subEntities(bulkDim); ++scvIdx)
-    //         {
-    //             auto globalI = problem_().model().bulkVertexMapper().subIndex(element, scvIdx, bulkDim);
-
-    //             for (auto&& globalJ : stencil)
-    //                 bulkPattern.add(globalI, globalJ);
-
-    //             for (auto&& globalJ : couplingStencil)
-    //                 bulkCouplingPattern.add(globalI, globalJ);
-    //         }
-    //     }
+    template <class T = BulkProblemTypeTag, typename std::enable_if<((GET_PROP_VALUE(T, ImplicitIsBox))), bool>::type = 0>
+    void buildBulkMatrixBlocks_(BulkMatrixBlock& m, BulkCouplingMatrixBlock& cm)
+    {
+        // get occupation pattern of the matrix
+        Dune::MatrixIndexSet bulkPattern, bulkCouplingPattern;
+        bulkPattern.resize(m.N(), m.M());
+        bulkCouplingPattern.resize(cm.N(), cm.M());
 
-    //     // export occupation patterns to matrices
-    //     bulkPattern.exportIdx(m);
-    //     bulkCouplingPattern.exportIdx(cm);
-    // }
+        for (const auto& element : elements(problem_().bulkGridView()))
+        {
+            const auto& couplingStencil = problem_().couplingManager().couplingStencil(element);
 
-    // void buildLowDimMatrixBlocksBox_(LowDimMatrixBlock& m, LowDimCouplingMatrixBlock& cm)
-    // {
-    //     // get occupation pattern of the matrix
-    //     Dune::MatrixIndexSet lowDimPattern, lowDimCouplingPattern;
-    //     lowDimPattern.resize(m.N(), m.M());
-    //     lowDimCouplingPattern.resize(cm.N(), cm.M());
+            for (unsigned int vIdx = 0; vIdx < element.subEntities(bulkDim); ++vIdx)
+            {
+                const auto globalI = problem_().model().bulkVertexMapper().subIndex(element, vIdx, bulkDim);
+                for (unsigned int vIdx2 = vIdx; vIdx2 < element.subEntities(bulkDim); ++vIdx2)
+                {
+                    const auto globalJ = problem_().model().bulkVertexMapper().subIndex(element, vIdx2, bulkDim);
+                    bulkPattern.add(globalI, globalJ);
+                    bulkPattern.add(globalJ, globalI);
+                }
+
+                for (auto&& globalJ : couplingStencil)
+                    bulkCouplingPattern.add(globalI, globalJ);
+
+                //TODO: additionalDofDependencies
+            }
+        }
 
-    //     for (const auto& element : elements(problem_().lowDimGridView()))
-    //     {
-    //         const auto& stencil = problem_().couplingManager().stencil(element);
-    //         const auto& couplingStencil = problem_().couplingManager().couplingStencil(element);
+        // export occupation patterns to matrices
+        bulkPattern.exportIdx(m);
+        bulkCouplingPattern.exportIdx(cm);
+    }
 
-    //         for (unsigned int scvIdx = 0; scvIdx < element.subEntities(lowDimDim); ++scvIdx)
-    //         {
-    //             auto globalI = problem_().model().lowDimVertexMapper().subIndex(element, scvIdx, lowDimDim);
+    template <class T = LowDimProblemTypeTag, typename std::enable_if<((GET_PROP_VALUE(T, ImplicitIsBox))), bool>::type = 0>
+    void buildLowDimMatrixBlocks_(LowDimMatrixBlock& m, LowDimCouplingMatrixBlock& cm)
+    {
+        // get occupation pattern of the matrix
+        Dune::MatrixIndexSet lowDimPattern, lowDimCouplingPattern;
+        lowDimPattern.resize(m.N(), m.M());
+        lowDimCouplingPattern.resize(cm.N(), cm.M());
 
-    //             for (auto&& globalJ : stencil)
-    //                 lowDimPattern.add(globalI, globalJ);
+        for (const auto& element : elements(problem_().lowDimGridView()))
+        {
+            const auto& couplingStencil = problem_().couplingManager().couplingStencil(element);
 
-    //             for (auto&& globalJ : couplingStencil)
-    //                 lowDimCouplingPattern.add(globalI, globalJ);
-    //         }
-    //     }
+            for (unsigned int vIdx = 0; vIdx < element.subEntities(lowDimDim); ++vIdx)
+            {
+                const auto globalI = problem_().model().lowDimVertexMapper().subIndex(element, vIdx, lowDimDim);
+                for (unsigned int vIdx2 = vIdx; vIdx2 < element.subEntities(lowDimDim); ++vIdx2)
+                {
+                    const auto globalJ = problem_().model().lowDimVertexMapper().subIndex(element, vIdx2, lowDimDim);
+                    lowDimPattern.add(globalI, globalJ);
+                    lowDimPattern.add(globalJ, globalI);
+                }
+
+                for (auto&& globalJ : couplingStencil)
+                    lowDimCouplingPattern.add(globalI, globalJ);
+
+                //TODO: additionalDofDependencies
+            }
+        }
 
-    //     // export occupation patterns to matrices
-    //     lowDimPattern.exportIdx(m);
-    //     lowDimCouplingPattern.exportIdx(cm);
-    // }
+        // export occupation patterns to matrices
+        lowDimPattern.exportIdx(m);
+        lowDimCouplingPattern.exportIdx(cm);
+    }
 
     // Construct the multitype matrix for the global jacobian
     void createMatrix_()
@@ -318,18 +330,8 @@ protected:
         auto A22 = LowDimMatrixBlock(lowDimSize, lowDimSize, LowDimMatrixBlock::random);
         auto A21 = LowDimCouplingMatrixBlock(lowDimSize, bulkSize, LowDimCouplingMatrixBlock::random);
 
-        // cell-centered
-        if (!bulkIsBox)
-            buildBulkMatrixBlocksCC_(A11, A12);
-        else{
-            // buildBulkMatrixBlocksBox_(A11, A12);
-        }
-
-        if (!lowDimIsBox)
-            buildLowDimMatrixBlocksCC_(A22, A21);
-        else{
-            // buildLowDimMatrixBlocksBox_(A22, A21);
-        }
+        buildBulkMatrixBlocks_(A11, A12);
+        buildLowDimMatrixBlocks_(A22, A21);
 
         (*matrix_)[bulkIdx][bulkIdx] = A11;
         (*matrix_)[bulkIdx][lowDimIdx] = A12;
diff --git a/dumux/mixeddimension/subproblemlocaljacobian.hh b/dumux/mixeddimension/subproblemlocaljacobian.hh
index 605be0a6a62fcadc2969e7b0084ca537c25720b5..43ddbb99cdf8466a1248a791eaabdbcd28672e56 100644
--- a/dumux/mixeddimension/subproblemlocaljacobian.hh
+++ b/dumux/mixeddimension/subproblemlocaljacobian.hh
@@ -104,6 +104,8 @@ class SubProblemLocalJacobian : public GET_PROP_TYPE(SubProblemTypeTag, LocalJac
     using Element = typename GridView::template Codim<0>::Entity;
     using SolutionVector = typename GET_PROP_TYPE(SubProblemTypeTag, SolutionVector);
 
+    enum { isBox = GET_PROP_VALUE(SubProblemTypeTag, ImplicitIsBox) };
+
 public:
 
     // copying a local jacobian is not a good idea
@@ -138,8 +140,6 @@ public:
                   JacobianMatrixCoupling& couplingMatrix,
                   SolutionVector& residual)
     {
-        const bool isGhost = (element.partitionType() == Dune::GhostEntity);
-
         // prepare the volvars/fvGeometries in case caching is disabled
         auto fvGeometry = localView(this->model_().globalFvGeometry());
         fvGeometry.bind(element);
@@ -153,13 +153,32 @@ public:
         auto elemFluxVarsCache = localView(this->model_().globalFluxVarsCache());
         elemFluxVarsCache.bind(element, fvGeometry, curElemVolVars);
 
-        // set the actual dof index
-        this->globalI_ = this->problem_().elementMapper().index(element);
-
         // check for boundaries on the element
         ElementBoundaryTypes elemBcTypes;
         elemBcTypes.update(this->problem_(), element, fvGeometry);
 
+        assemble_(element, fvGeometry, prevElemVolVars, curElemVolVars, elemFluxVarsCache, elemBcTypes, matrix, couplingMatrix, residual);
+    }
+
+protected:
+
+    // for cell-centered models
+    template<class JacobianMatrix, class JacobianMatrixCoupling, class T = SubProblemTypeTag, typename std::enable_if<((!GET_PROP_VALUE(T, ImplicitIsBox))), bool>::type = 0>
+    void assemble_(const Element& element,
+                     const FVElementGeometry& fvGeometry,
+                     ElementVolumeVariables& prevElemVolVars,
+                     ElementVolumeVariables& curElemVolVars,
+                     ElementFluxVariablesCache& elemFluxVarsCache,
+                     const ElementBoundaryTypes& elemBcTypes,
+                     JacobianMatrix& matrix,
+                     JacobianMatrixCoupling& couplingMatrix,
+                     SolutionVector& residual)
+    {
+        const bool isGhost = (element.partitionType() == Dune::GhostEntity);
+
+        // set the actual dof index
+        this->globalI_ = this->problem_().elementMapper().index(element);
+
         // Evaluate the undeflected element local residual
         this->localResidual().eval(element,
                                    fvGeometry,
@@ -204,7 +223,73 @@ public:
                                        residual);
     }
 
-protected:
+    // for box models
+    template<class JacobianMatrix, class JacobianMatrixCoupling, class T = SubProblemTypeTag, typename std::enable_if<((GET_PROP_VALUE(T, ImplicitIsBox))), bool>::type = 0>
+    void assemble_(const Element& element,
+                     const FVElementGeometry& fvGeometry,
+                     ElementVolumeVariables& prevElemVolVars,
+                     ElementVolumeVariables& curElemVolVars,
+                     ElementFluxVariablesCache& elemFluxVarsCache,
+                     const ElementBoundaryTypes& elemBcTypes,
+                     JacobianMatrix& matrix,
+                     JacobianMatrixCoupling& couplingMatrix,
+                     SolutionVector& residual)
+    {
+        constexpr auto numEq = GET_PROP_VALUE(T, NumEq);
+
+        // the element solution
+        auto curElemSol = this->problem_().model().elementSolution(element, this->problem_().model().curSol());
+
+        // calculate the actual element residual
+        this->localResidual().eval(element, fvGeometry, prevElemVolVars, curElemVolVars, elemBcTypes, elemFluxVarsCache);
+        this->residual_ = this->localResidual().residual();
+
+        // residual[this->globalI_] = this->localResidual().residual(0);
+
+        this->problem_().model().updatePVWeights(fvGeometry);
+
+        // calculation of the derivatives
+        for (auto&& scv : scvs(fvGeometry))
+        {
+            // dof index and corresponding actual pri vars
+            const auto dofIdx = scv.dofIndex();
+            auto& curVolVars = this->getCurVolVars(curElemVolVars, scv);
+            VolumeVariables origVolVars(curVolVars);
+
+            // add precalculated residual for this scv into the global container
+            residual[dofIdx] += this->residual_[scv.index()];
+
+            // calculate derivatives w.r.t to the privars at the dof at hand
+            for (int pvIdx = 0; pvIdx < numEq; pvIdx++)
+            {
+                this->evalPartialDerivative_(matrix,
+                                       element,
+                                       fvGeometry,
+                                       prevElemVolVars,
+                                       curElemVolVars,
+                                       curElemSol,
+                                       scv,
+                                       elemBcTypes,
+                                       elemFluxVarsCache,
+                                       pvIdx);
+
+                // restore the original state of the scv's volume variables
+                curVolVars = origVolVars;
+
+                // restore the original element solution
+                curElemSol[scv.index()][pvIdx] = this->problem_().model().curSol()[scv.dofIndex()][pvIdx];
+            }
+            // TODO: what if we have an extended source stencil????
+        }
+        //TODO: is otherElement in this method required? is otherResidual correct?
+        evalPartialDerivativeCouplingBox_(element,
+                                       fvGeometry,
+                                       curElemVolVars,
+                                       elemFluxVarsCache,
+                                       elemBcTypes,
+                                       couplingMatrix,
+                                       residual);
+    }
 
     /*!
      * \brief Returns a reference to the problem.
@@ -234,6 +319,7 @@ protected:
     decltype(auto) otherProblem_(typename std::enable_if<!std::is_same<typename GET_PROP_TYPE(T, LowDimProblemTypeTag), SubProblemTypeTag>::value, void>::type* x = nullptr)
     { return globalProblem_().lowDimProblem(); }
 
+    // cell-centered
     template<class JacobianMatrixCoupling>
     void evalPartialDerivativeCoupling_(const Element& element,
                                         const FVElementGeometry& fvGeometry,
@@ -329,6 +415,102 @@ protected:
         }
     }
 
+    // box
+    template<class JacobianMatrixCoupling>
+    void evalPartialDerivativeCouplingBox_(const Element& element,
+                                        const FVElementGeometry& fvGeometry,
+                                        ElementVolumeVariables& curElemVolVars,
+                                        ElementFluxVariablesCache& elemFluxVarsCache,
+                                        const ElementBoundaryTypes& elemBcTypes,
+                                        JacobianMatrixCoupling& couplingMatrix,
+                                        SolutionVector& residual)
+    {
+        const auto& couplingStencil = globalProblem_().couplingManager().couplingStencil(element);
+        constexpr auto numEq = GET_PROP_VALUE(SubProblemTypeTag, NumEq);
+
+        for (auto globalJ : couplingStencil)
+        {
+            const auto otherResidual = globalProblem_().couplingManager().evalCouplingResidual(element,
+                                                                                               fvGeometry,
+                                                                                               curElemVolVars,
+                                                                                               elemBcTypes,
+                                                                                               elemFluxVarsCache);
+
+            auto& otherPriVars = otherProblem_().model().curSol()[globalJ];
+            auto originalOtherPriVars = otherPriVars;
+
+            // derivatives in the neighbors with repect to the current elements
+            ElementSolutionVector partialDeriv(fvGeometry.numScv());
+            for (int pvIdx = 0; pvIdx < numEq; pvIdx++)
+            {
+                const Scalar eps = this->numericEpsilon(otherPriVars[pvIdx]);
+                Scalar delta = 0;
+
+                if (numericDifferenceMethod_ >= 0)
+                {
+                    // we are not using backward differences, i.e. we need to
+                    // calculate f(x + \epsilon)
+
+                    // deflect primary variables
+                    otherPriVars[pvIdx] += eps;
+                    delta += eps;
+
+                    // calculate the residual with the deflected primary variables
+                    partialDeriv = globalProblem_().couplingManager().evalCouplingResidual(element,
+                                                                                           fvGeometry,
+                                                                                           curElemVolVars,
+                                                                                           elemBcTypes,
+                                                                                           elemFluxVarsCache);
+                }
+                else
+                {
+                    // we are using backward differences, i.e. we don't need
+                    // to calculate f(x + \epsilon) and we can recycle the
+                    // (already calculated) residual f(x)
+                    partialDeriv = otherResidual;
+                }
+
+
+                if (numericDifferenceMethod_ <= 0)
+                {
+                    // we are not using forward differences, i.e. we
+                    // need to calculate f(x - \epsilon)
+
+                    // deflect the primary variables
+                    otherPriVars[pvIdx] -= 2*eps;
+                    delta += eps;
+
+                    // calculate the residual with the deflected primary variables
+                    partialDeriv -= globalProblem_().couplingManager().evalCouplingResidual(element,
+                                                                                            fvGeometry,
+                                                                                            curElemVolVars,
+                                                                                            elemBcTypes,
+                                                                                            elemFluxVarsCache);
+                }
+                else
+                {
+                    // we are using forward differences, i.e. we don't need to
+                    // calculate f(x - \epsilon) and we can recycle the
+                    // (already calculated) residual f(x)
+                    partialDeriv -= otherResidual;
+                }
+
+                // divide difference in residuals by the magnitude of the
+                // deflections between the two function evaluation
+                partialDeriv /= delta;
+
+                // restore the original state of the element solution vector
+                otherPriVars = originalOtherPriVars;
+
+                // update the global jacobian matrix (coupling block)
+                // this->updateGlobalJacobian_(couplingMatrix, scv.dofIndex(), globalJ, pvIdx, partialDeriv[scv.dofIndex()]);
+
+                for (auto&& scv : scvs(fvGeometry))
+                    this->updateGlobalJacobian_(couplingMatrix, scv.dofIndex(), globalJ, pvIdx, partialDeriv[scv.index()]);
+            }
+        }
+    }
+
     // The problem we would like to solve
     GlobalProblem *globalProblemPtr_;
     // The type of the numeric difference method (forward, center, backward)