diff --git a/modcc/module.cpp b/modcc/module.cpp
index 5e46c307cbea98b930d18fa7dbbca9b5c5a668f0..56d22f0326aad8df5b80736d76950a2a71864688 100644
--- a/modcc/module.cpp
+++ b/modcc/module.cpp
@@ -151,7 +151,7 @@ bool Module::semantic() {
     // symbol table.
     // Returns false if a symbol name clashes with the name of a symbol that
     // is already in the symbol table.
-    bool linear = true;
+    bool linear_homogeneous = true;
     std::vector<std::string> state_vars;
     auto move_symbols = [this] (std::vector<symbol_ptr>& symbol_list) {
         for(auto& symbol: symbol_list) {
@@ -388,61 +388,56 @@ bool Module::semantic() {
             continue;
         }
         found_solve = true;
-        std::unique_ptr<SolverVisitorBase> solver;
 
+        // If the derivative block is a kinetic block, perform kinetic rewrite first.
+        auto deriv = solve_expression->procedure();
+        auto solve_body = deriv->body()->clone();
+        if (deriv->kind()==procedureKind::kinetic) {
+            solve_body = kinetic_rewrite(deriv->body());
+        }
+        else if (deriv->kind()==procedureKind::linear) {
+            solve_body = linear_rewrite(deriv->body(), state_vars);
+        }
+
+        // Calculate linearity and homogeneity of the statements in the derivative block.
+        bool linear = true;
+        bool homogeneous = true;
+        for (auto& s: solve_body->is_block()->statements()) {
+            if(s->is_assignment() && !state_vars.empty()) {
+                linear_test_result r = linear_test(s->is_assignment()->rhs(), state_vars);
+                linear &= r.is_linear;
+                homogeneous &= r.is_homogeneous;
+            }
+        }
+        linear_homogeneous &= (linear & homogeneous);
+
+        // Construct solver based on system kind, linearity and solver method.
+        std::unique_ptr<SolverVisitorBase> solver;
         switch(solve_expression->method()) {
         case solverMethod::cnexp:
             solver = std::make_unique<CnexpSolverVisitor>();
             break;
         case solverMethod::sparse: {
-            solver = std::make_unique<SparseSolverVisitor>(solve_expression->variant());
-            break;
-        }
-        case solverMethod::none:
-            solver = std::make_unique<DirectSolverVisitor>();
-            break;
-        }
-
-        // If the derivative block is a kinetic block, perform kinetic
-        // rewrite first.
-
-        auto deriv = solve_expression->procedure();
-
-        if (deriv->kind()==procedureKind::kinetic) {
-            auto rewrite_body = kinetic_rewrite(deriv->body());
-            bool linear_kinetic = true;
-
-            for (auto& s: rewrite_body->is_block()->statements()) {
-                if(s->is_assignment() && !state_vars.empty()) {
-                    linear_test_result r = linear_test(s->is_assignment()->rhs(), state_vars);
-                    linear_kinetic &= r.is_linear;
-                }
+            if (linear) {
+                solver = std::make_unique<SparseSolverVisitor>(solve_expression->variant());
             }
-
-            if (!linear_kinetic) {
+            else {
                 solver = std::make_unique<SparseNonlinearSolverVisitor>();
             }
-
-            rewrite_body->semantic(advance_state_scope);
-            rewrite_body->accept(solver.get());
-        }
-        else if (deriv->kind()==procedureKind::linear) {
-            solver = std::make_unique<LinearSolverVisitor>(state_vars);
-            auto rewrite_body = linear_rewrite(deriv->body(), state_vars);
-
-            rewrite_body->semantic(advance_state_scope);
-            rewrite_body->accept(solver.get());
+            break;
         }
-        else {
-            deriv->body()->accept(solver.get());
-            for (auto& s: deriv->body()->statements()) {
-                if(s->is_assignment() && !state_vars.empty()) {
-                    linear_test_result r = linear_test(s->is_assignment()->rhs(), state_vars);
-                    linear &= r.is_linear;
-                    linear &= r.is_homogeneous;
-                }
+        case solverMethod::none:
+            if (deriv->kind()==procedureKind::linear) {
+                solver = std::make_unique<LinearSolverVisitor>(state_vars);
             }
+            else {
+                solver = std::make_unique<DirectSolverVisitor>();
+            }
+            break;
         }
+        // Perform semantic analysis on the solve block statements and solve them.
+        solve_body->semantic(advance_state_scope);
+        solve_body->accept(solver.get());
 
         if (auto solve_block = solver->as_block(false)) {
             // Check that we didn't solve an already solved variable.
@@ -490,8 +485,8 @@ bool Module::semantic() {
     for (auto& s: breakpoint->body()->statements()) {
         if(s->is_assignment() && !state_vars.empty()) {
             linear_test_result r = linear_test(s->is_assignment()->rhs(), state_vars);
-            linear &= r.is_linear;
-            linear &= r.is_homogeneous;
+            linear_homogeneous &= r.is_linear;
+            linear_homogeneous &= r.is_homogeneous;
         }
     }
 
@@ -517,7 +512,7 @@ bool Module::semantic() {
                     for (const auto &id: state_vars) {
                         auto coef = symbolic_pdiff(s->is_assignment()->rhs(), id);
                         if(!coef) {
-                            linear = false;
+                            linear_homogeneous = false;
                             continue;
                         }
                         if(coef->is_number()) {
@@ -525,19 +520,19 @@ bool Module::semantic() {
                                 error(pprintf("Left hand side of assignment is not an identifier"));
                                 return false;
                             }
-                            linear &= s->is_assignment()->lhs()->is_identifier()->name() == id ?
-                                      coef->is_number()->value() == 1 :
-                                      coef->is_number()->value() == 0;
+                            linear_homogeneous &= s->is_assignment()->lhs()->is_identifier()->name() == id ?
+                                                  coef->is_number()->value() == 1 :
+                                                  coef->is_number()->value() == 0;
                         }
                         else {
-                            linear = false;
+                            linear_homogeneous = false;
                         }
                     }
                 }
             }
         }
     }
-    linear_ = linear;
+    linear_ = linear_homogeneous;
 
     post_events_ = has_symbol("post_event", symbolKind::procedure);
     if (post_events_) {
diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt
index dbb6ddfa72f2aadf7c825d3f0323cba2f68b6c43..ef278313305f2c2534eeddf944ff485c044e5646 100644
--- a/test/unit/CMakeLists.txt
+++ b/test/unit/CMakeLists.txt
@@ -25,6 +25,8 @@ set(test_mechanisms
     test2_kin_diff
     test3_kin_diff
     test4_kin_compartment
+    test5_nonlinear_diff
+    test6_nonlinear_diff
     test_ca
     test_ca_read_valence
     test_cl_valence
diff --git a/test/unit/mod/test4_kin_compartment.mod b/test/unit/mod/test4_kin_compartment.mod
index 28bc29897eced1dcd00828639eee5623f3262150..3eaf55e4e47e078324363be391697a5325dae5fa 100644
--- a/test/unit/mod/test4_kin_compartment.mod
+++ b/test/unit/mod/test4_kin_compartment.mod
@@ -37,5 +37,5 @@ KINETIC state {
     COMPARTMENT s1 {d e}
 
     ~ A + B <-> C   ( x, y )
-	~ A + d <-> e   ( z, w )
+    ~ A + d <-> e   ( z, w )
 }
\ No newline at end of file
diff --git a/test/unit/mod/test5_nonlinear_diff.mod b/test/unit/mod/test5_nonlinear_diff.mod
new file mode 100644
index 0000000000000000000000000000000000000000..d9be37dcb607c05d3f94a447966298e6c654afa8
--- /dev/null
+++ b/test/unit/mod/test5_nonlinear_diff.mod
@@ -0,0 +1,29 @@
+NEURON {
+    SUFFIX test5_nonlinear_diff
+}
+
+STATE {
+    a b c
+}
+
+BREAKPOINT {
+    SOLVE state METHOD sparse
+}
+
+DERIVATIVE state {
+    LOCAL f0, f1, r0, r1
+    f0 = 2
+    r0 = 1
+    f1 = 3
+    r1 = 0
+
+    a' = -f0*a*b + r0*c
+    b' = -f0*a*b -r1*b + (r0+f1)*c
+    c' =  f0*a*b +r1*b - (r0+f1)*c
+}
+
+INITIAL {
+    a = 0.2
+    b = 0.3
+    c = 0.5
+}
diff --git a/test/unit/mod/test6_nonlinear_diff.mod b/test/unit/mod/test6_nonlinear_diff.mod
new file mode 100644
index 0000000000000000000000000000000000000000..cb8946cb2271596dacbb65fa871c591c1087ec5b
--- /dev/null
+++ b/test/unit/mod/test6_nonlinear_diff.mod
@@ -0,0 +1,19 @@
+NEURON {
+    SUFFIX test6_nonlinear_diff
+}
+
+STATE {
+    p
+}
+
+BREAKPOINT {
+    SOLVE state METHOD sparse
+}
+
+DERIVATIVE state {
+    p' = sin(p)
+}
+
+INITIAL {
+    p = 1
+}
diff --git a/test/unit/test_kinetic_linear.cpp b/test/unit/test_kinetic_linear.cpp
index 45a1330f8307743590731781ebea6c7cdca0c916..3b2e9cf99b44f9888c4e351ee3d591fcd92f36bf 100644
--- a/test/unit/test_kinetic_linear.cpp
+++ b/test/unit/test_kinetic_linear.cpp
@@ -127,6 +127,20 @@ TEST(mech_kinetic, kinetic_nonlinear) {
 
 }
 
+TEST(mech_kinetic, normal_nonlinear_0) {
+    std::vector<std::string> state_variables = {"a", "b", "c"};
+    std::vector<fvm_value_type> t0_values = {0.2, 0.3, 0.5};
+    std::vector<fvm_value_type> t1_values = {0.2078873133, 0.34222075, 0.45777925};
+    run_test<multicore::backend>("test5_nonlinear_diff", state_variables, t0_values, t1_values, 0.025);
+}
+
+TEST(mech_kinetic, normal_nonlinear_1) {
+    std::vector<std::string> state_variables = {"p"};
+    std::vector<fvm_value_type> t0_values = {1};
+    std::vector<fvm_value_type> t1_values = {1.0213199524};
+    run_test<multicore::backend>("test6_nonlinear_diff", state_variables, t0_values, t1_values, 0.025);
+}
+
 TEST(mech_kinetic, kinetic_nonlinear_scaled) {
     std::vector<std::string> state_variables = {"A", "B", "C", "d", "e"};
     std::vector<fvm_value_type> t0_values = {4.5, 6.6, 0.28, 2, 0};
@@ -191,6 +205,20 @@ TEST(mech_kinetic_gpu, kinetic_nonlinear) {
     run_test<gpu::backend>("test3_kin_diff", state_variables, t0_values, t1_1_values, 0.025);
 }
 
+TEST(mech_kinetic_gpu, normal_nonlinear_0) {
+    std::vector<std::string> state_variables = {"a", "b", "c"};
+    std::vector<fvm_value_type> t0_values = {0.2, 0.3, 0.5};
+    std::vector<fvm_value_type> t1_values = {0.2078873133, 0.34222075, 0.45777925};
+    run_test<gpu::backend>("test5_nonlinear_diff", state_variables, t0_values, t1_values, 0.025);
+}
+
+TEST(mech_kinetic_gpu, normal_nonlinear_1) {
+    std::vector<std::string> state_variables = {"p"};
+    std::vector<fvm_value_type> t0_values = {1};
+    std::vector<fvm_value_type> t1_values = {1.0213199524};
+    run_test<gpu::backend>("test6_nonlinear_diff", state_variables, t0_values, t1_values, 0.025);
+}
+
 TEST(mech_kinetic_gpu, kinetic_nonlinear_scaled) {
     std::vector<std::string> state_variables = {"A", "B", "C", "d", "e"};
     std::vector<fvm_value_type> t0_values = {4.5, 6.6, 0.28, 2, 0};
diff --git a/test/unit/unit_test_catalogue.cpp b/test/unit/unit_test_catalogue.cpp
index d9e3adb366c9f9ece0cd87e9738df5432477d89f..bd04e05099bdff2e26474cd612dbf45736cdba9c 100644
--- a/test/unit/unit_test_catalogue.cpp
+++ b/test/unit/unit_test_catalogue.cpp
@@ -28,6 +28,8 @@
 #include "mechanisms/test2_kin_diff.hpp"
 #include "mechanisms/test3_kin_diff.hpp"
 #include "mechanisms/test4_kin_compartment.hpp"
+#include "mechanisms/test5_nonlinear_diff.hpp"
+#include "mechanisms/test6_nonlinear_diff.hpp"
 #include "mechanisms/test1_kin_steadystate.hpp"
 #include "mechanisms/fixed_ica_current.hpp"
 #include "mechanisms/point_ica_current.hpp"
@@ -86,6 +88,8 @@ mechanism_catalogue make_unit_test_catalogue(const mechanism_catalogue& from) {
     ADD_MECH(cat, test1_kin_steadystate)
     ADD_MECH(cat, test1_kin_compartment)
     ADD_MECH(cat, test4_kin_compartment)
+    ADD_MECH(cat, test5_nonlinear_diff)
+    ADD_MECH(cat, test6_nonlinear_diff)
     ADD_MECH(cat, fixed_ica_current)
     ADD_MECH(cat, non_linear)
     ADD_MECH(cat, point_ica_current)