diff --git a/generate_all_operators.py b/generate_all_operators.py
index 05c7ee2dabc57b02e0b3baf93805bfb13d833b73..e8b67fe6c4d0d32b4be143f3ec971280a13d8d4c 100644
--- a/generate_all_operators.py
+++ b/generate_all_operators.py
@@ -51,8 +51,9 @@ from hog.forms import (
     supg_diffusion,
     advection,
     supg_advection,
-    grad_rho_by_rho_dot_u, nonlinear_diffusion_newton_galerkin_symmetric_part,
-    nonlinear_diffusion_newton_galerkin_asymmetric_part, nonlinear_diffusion_newton_galerkin_asymmetric_lumped_part,
+    grad_rho_by_rho_dot_u,
+    nonlinear_diffusion_stiffness,
+    nonlinear_diffusion_newton_galerkin_approximated_mass_lumped,
 )
 from hog.forms_boundary import (
     mass_boundary,
@@ -73,7 +74,7 @@ from hog.operator_generation.kernel_types import (
     ApplyWrapper,
     AssembleWrapper,
     AssembleDiagonalWrapper,
-    KernelWrapperType,
+    KernelWrapperType, LumpWrapper,
 )
 from hog.operator_generation.loop_strategies import (
     LoopStrategy,
@@ -573,6 +574,14 @@ class OperatorInfo:
                     )
                 )
 
+            self.kernel_types.append(
+                LumpWrapper(
+                    self.trial_space,
+                    type_descriptor=self.type_descriptor,
+                    dims=dims,
+                )
+            )
+
 
 def all_operators(
     symbolizer: Symbolizer,
@@ -620,20 +629,24 @@ def all_operators(
     ops.append(OperatorInfo("P1", "NonlinearDiffusion", TrialSpace(P1), TestSpace(P1),
                             form=partial(nonlinear_diffusion, coefficient_function_space=P1),
                             type_descriptor=type_descriptor, geometries=list(geometries), opts=opts, blending=blending))
+
     ops.append(OperatorInfo("P1", "NonlinearDiffusionNewtonGalerkin", TrialSpace(P1),
                             TestSpace(P1), form=partial(nonlinear_diffusion_newton_galerkin,
-                            coefficient_function_space=P1, only_newton_galerkin_part_of_form=False),
+                            coefficient_function_space=P1),
                             type_descriptor=type_descriptor, geometries=list(geometries), opts=opts, blending=blending))
-    ops.append(OperatorInfo("P1", "NonlinearDiffusionNewtonGalerkinSymmetricPart", TrialSpace(P1),
-                            TestSpace(P1), form=partial(nonlinear_diffusion_newton_galerkin_symmetric_part,
-                                                        coefficient_function_space=P1),
+
+    ops.append(OperatorInfo("P1", "NonlinearDiffusionNewtonGalerkinStiffness", TrialSpace(P1),
+                            TestSpace(P1), form=partial(nonlinear_diffusion_stiffness,
+                            coefficient_function_space=P1),
                             type_descriptor=type_descriptor, geometries=list(geometries), opts=opts, blending=blending))
-    ops.append(OperatorInfo("P1", "NonlinearDiffusionNewtonGalerkinAsymmetricPart", TrialSpace(P1),
-                            TestSpace(P1), form=partial(nonlinear_diffusion_newton_galerkin_asymmetric_part,
-                                                        coefficient_function_space=P1),
+
+    ops.append(OperatorInfo("P1", "NonlinearDiffusionNewtonGalerkinStiffnessDifferentiated", TrialSpace(P1),
+                            TestSpace(P1), form=partial(nonlinear_diffusion_stiffness,
+                            coefficient_function_space=P1, use_a_derivative=True),
                             type_descriptor=type_descriptor, geometries=list(geometries), opts=opts, blending=blending))
-    ops.append(OperatorInfo("P1", "NonlinearDiffusionNewtonGalerkinAsymmetricLumpedPart", TrialSpace(P1),
-                            TestSpace(P1), form=partial(nonlinear_diffusion_newton_galerkin_asymmetric_lumped_part,
+
+    ops.append(OperatorInfo("P1", "NonlinearDiffusionNewtonGalerkinApproximatedMassLumped", TrialSpace(P1),
+                            TestSpace(P1), form=partial(nonlinear_diffusion_newton_galerkin_approximated_mass_lumped,
                                                         coefficient_function_space=P1),
                             type_descriptor=type_descriptor, geometries=list(geometries), opts=opts, blending=blending))
 
diff --git a/hog/forms.py b/hog/forms.py
index fdee07a0c06ea7cd4545f6c6cf998b4781543149..b3338d8c6b98736c66ba8d97cef869368b8ddd6e 100644
--- a/hog/forms.py
+++ b/hog/forms.py
@@ -247,7 +247,6 @@ def nonlinear_diffusion_newton_galerkin(
     symbolizer: Symbolizer,
     coefficient_function_space: FunctionSpace,
     blending: GeometryMap = IdentityMap(),
-    only_newton_galerkin_part_of_form: Optional[bool] = True,
 ) -> Form:
     docstring = f"""
 
@@ -299,10 +298,7 @@ Note: :math:`a(k) = 1/8 + k^2` is currently hard-coded and the form is intended
             * tabulate(jac_a_abs_det)
         )
 
-        if only_newton_galerkin_part_of_form:
-            return newton_galerkin_term
-        else:
-            return diffusion_term + newton_galerkin_term
+        return diffusion_term + newton_galerkin_term
 
     return process_integrand(
         integrand,
@@ -316,19 +312,17 @@ Note: :math:`a(k) = 1/8 + k^2` is currently hard-coded and the form is intended
         fe_coefficients={"k": coefficient_function_space},
     )
 
-
-def nonlinear_diffusion_newton_galerkin_symmetric_part(
-    trial: TrialSpace,
-    test: TestSpace,
-    geometry: ElementGeometry,
-    symbolizer: Symbolizer,
-    coefficient_function_space: FunctionSpace,
-    blending: GeometryMap = IdentityMap(),
+def nonlinear_diffusion_stiffness(
+        trial: TrialSpace,
+        test: TestSpace,
+        geometry: ElementGeometry,
+        symbolizer: Symbolizer,
+        coefficient_function_space: FunctionSpace,
+        blending: GeometryMap = IdentityMap(),
+        use_a_derivative: bool = False,
 ) -> Form:
     docstring = f"""
-todo: heavy code duplication, this can be implemented with small changes to nonlinear_diffusion_newton_galerkin 
-
-Symmetric part of the bi-linear form for the solution of the non-linear diffusion equation by a Newton-Galerkin approach
+todo
 
 Weak formulation
 
@@ -337,9 +331,12 @@ Weak formulation
     k: FE coefficient function (space: {coefficient_function_space})
 
     ∫ a(k) ∇u · ∇v
+    
+    or ∫ a'(k) ∇u · ∇v if use_a_derivative was set
 
 Note: :math:`a(k) = 1/8 + k^2` is currently hard-coded and the form is intended for :math:`k = u`.
 """
+
     if trial != test:
         raise HOGException(
             "Trial space must be equal to test space to assemble diffusion matrix."
@@ -351,18 +348,24 @@ Note: :math:`a(k) = 1/8 + k^2` is currently hard-coded and the form is intended
         )
 
     def integrand(
-        *,
-        jac_a_inv,
-        jac_a_abs_det,
-        grad_u,
-        grad_v,
-        k,
-        tabulate,
-        **_,
+            *,
+            jac_a_inv,
+            jac_a_abs_det,
+            grad_u,
+            grad_v,
+            k,
+            tabulate,
+            **_,
     ):
         a = sp.Rational(1, 8) + k["k"] * k["k"]
+        a_prime = 2 * k["k"]
 
-        diffusion_term = a * tabulate(
+        if use_a_derivative:
+            used_a = a_prime
+        else:
+            used_a = a
+
+        diffusion_term = used_a * tabulate(
             dot(jac_a_inv.T * grad_u, jac_a_inv.T * grad_v) * jac_a_abs_det
         )
 
@@ -380,20 +383,19 @@ Note: :math:`a(k) = 1/8 + k^2` is currently hard-coded and the form is intended
         fe_coefficients={"k": coefficient_function_space},
     )
 
-
-def nonlinear_diffusion_newton_galerkin_asymmetric_part(
-    trial: TrialSpace,
-    test: TestSpace,
-    geometry: ElementGeometry,
-    symbolizer: Symbolizer,
-    coefficient_function_space: FunctionSpace,
-    blending: GeometryMap = IdentityMap(),
+def nonlinear_diffusion_newton_galerkin_approximated_mass_lumped(
+        trial: TrialSpace,
+        test: TestSpace,
+        geometry: ElementGeometry,
+        symbolizer: Symbolizer,
+        coefficient_function_space: FunctionSpace,
+        blending: GeometryMap = IdentityMap(),
 ) -> Form:
     docstring = f"""
 
-todo: heavy code duplication, this can be implemented with small changes to nonlinear_diffusion_newton_galerkin 
+Bi-linear form with approximated mass lumping on the asymmetric part for the solution of the non-linear diffusion equation by a Newton-Galerkin approach
+This just uses the approximation 9.25 from Larson & Bengzon. This is not the actual mass lumping approach.
 
-Asymmetric part of bi-linear form for the solution of the non-linear diffusion equation by a Newton-Galerkin approach
 
 Weak formulation
 
@@ -401,7 +403,9 @@ Weak formulation
     v: test function  (space: {test})
     k: FE coefficient function (space: {coefficient_function_space})
 
-    ∫ a'(k) u ∇k · ∇v
+    ∫ a(k) ∇u · ∇v + δ(i,j) · ∫ a'(k) ∇k · ∇v
+    where δ(i, j) = if i == j then 1 else 0, and i, j are the indices of the corresponding matrix element
+    i.e. δ = 1 on the diagonal and 0 otherwise
 
 Note: :math:`a(k) = 1/8 + k^2` is currently hard-coded and the form is intended for :math:`k = u`.
 """
@@ -416,100 +420,36 @@ Note: :math:`a(k) = 1/8 + k^2` is currently hard-coded and the form is intended
         )
 
     def integrand(
-        *,
-        jac_a_inv,
-        jac_a_abs_det,
-        u,
-        grad_v,
-        k,
-        grad_k,
-        tabulate,
-        **_,
+            *,
+            jac_a_inv,
+            jac_a_abs_det,
+            grad_u,
+            grad_v,
+            k,
+            grad_k,
+            tabulate,
+            row,
+            col,
+            **_,
     ):
+        a = sp.Rational(1, 8) + k["k"] * k["k"]
         a_prime = 2 * k["k"]
 
-        newton_galerkin_term = (
-            a_prime
-            * u
-            * dot(jac_a_inv.T * grad_k["k"], tabulate(jac_a_inv.T * grad_v))
-            * tabulate(jac_a_abs_det)
-        )
-
-        return newton_galerkin_term
-
-    return process_integrand(
-        integrand,
-        trial,
-        test,
-        geometry,
-        symbolizer,
-        blending=blending,
-        is_symmetric=False,
-        docstring=docstring,
-        fe_coefficients={"k": coefficient_function_space},
-    )
-
-
-def nonlinear_diffusion_newton_galerkin_asymmetric_lumped_part(
-    trial: TrialSpace,
-    test: TestSpace,
-    geometry: ElementGeometry,
-    symbolizer: Symbolizer,
-    coefficient_function_space: FunctionSpace,
-    blending: GeometryMap = IdentityMap(),
-) -> Form:
-    docstring = f"""
-
-todo: heavy code duplication, this can be implemented with small changes to nonlinear_diffusion_newton_galerkin 
-
-Asymmetric part of bi-linear form for the solution of the non-linear diffusion equation by a Newton-Galerkin approach. Optimized with mass lumping.
-
-Weak formulation
-
-    u: trial function (space: {trial})
-    v: test function  (space: {test})
-    k: FE coefficient function (space: {coefficient_function_space})
-
-    ∫ a'(k) u ∇k · ∇v
-
-Note: :math:`a(k) = 1/8 + k^2` is currently hard-coded and the form is intended for :math:`k = u`.
-"""
-    if trial != test:
-        raise HOGException(
-            "Trial space must be equal to test space to assemble diffusion matrix."
-        )
-
-    if blending != IdentityMap():
-        raise HOGException(
-            "The nonlinear_diffusion_newton_galerkin form does currently not support blending."
+        diffusion_term = a * tabulate(
+            dot(jac_a_inv.T * grad_u, jac_a_inv.T * grad_v) * jac_a_abs_det
         )
 
-    def integrand(
-        *,
-        jac_a_inv,
-        jac_a_abs_det,
-        u,
-        v,
-        grad_v,
-        k,
-        grad_k,
-        tabulate,
-        **_,
-    ):
-        # todo this turns into a diagonal matrix, can this further get optimized?
-        # todo can you get the indices of u and v and compare those instead of the whole expressions?
-        if not u.equals(v):
-            return 0
-        else:
-            a_prime = 2 * k["k"]
-
-            newton_galerkin_term = (
+        approximated_newton_galerkin_term = (
                 a_prime
                 * dot(jac_a_inv.T * grad_k["k"], tabulate(jac_a_inv.T * grad_v))
                 * tabulate(jac_a_abs_det)
-            )
+        )
+
+        if row != col:
+            return diffusion_term
+        else:
+            return diffusion_term + approximated_newton_galerkin_term
 
-            return newton_galerkin_term
 
     return process_integrand(
         integrand,
@@ -518,7 +458,7 @@ Note: :math:`a(k) = 1/8 + k^2` is currently hard-coded and the form is intended
         geometry,
         symbolizer,
         blending=blending,
-        is_symmetric=False,
+        is_symmetric=True,
         docstring=docstring,
         fe_coefficients={"k": coefficient_function_space},
     )
diff --git a/hog/integrand.py b/hog/integrand.py
index 07313705041256cc2c0ffb8f2f35d62ee448dbbc..2637677ed97a940582f4bb9dc7836300f3da8118 100644
--- a/hog/integrand.py
+++ b/hog/integrand.py
@@ -491,7 +491,7 @@ def process_integrand(
         s.grad_v = data.test_shape_grad
         s.hessian_v = data.test_shape_hessian
 
-        mat[data.row, data.col] = integrand(**asdict(s))
+        mat[data.row, data.col] = integrand(**asdict(s), row=data.row, col=data.col)
 
     free_symbols_sorted = sorted(list(free_symbols), key=lambda x: str(x))