diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5e0068e5d4189093c358bf34c9987706d5815f1a..8d3def61aa2a8264b2b7670d93e9d348c45ea5d3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -253,6 +253,8 @@ set(LIBGMSHDG_SOURCES src/gmsh_io.cpp
                       src/kernels_gpu_mi.cpp
                       src/kernels_cuda.cu
                       src/maxwell_interface.cpp
+                      src/maxwell_cpu.cpp
+                      src/maxwell_gpu.cpp
                     )
 
 add_library(gmshdg SHARED ${LIBGMSHDG_SOURCES})
diff --git a/include/maxwell_interface.h b/include/maxwell_interface.h
index 2481d886eec061ed99bb2d9e921520fd30497b4e..371b57d401e1936e3523754538963d96bdefbbf2 100644
--- a/include/maxwell_interface.h
+++ b/include/maxwell_interface.h
@@ -1,9 +1,12 @@
 #pragma once
 
+#include "gmsh_io.h"
 #include "types.h"
 #include "gpu_support.hpp"
 
-struct electromagnetic_field
+namespace maxwell {
+
+struct field
 {
     vecxd   Ex;
     vecxd   Ey;
@@ -17,7 +20,7 @@ struct electromagnetic_field
     void    resize(size_t);
 };
 
-struct electromagnetic_material_params
+struct material_params
 {
     size_t  num_dofs;
     size_t  num_fluxes;
@@ -37,7 +40,7 @@ struct electromagnetic_material_params
     vecxd   bc_coeffs;
 };
 
-struct electromagnetic_material_params_gpu
+struct material_params_gpu
 {
     size_t  num_dofs;
     size_t  num_fluxes;
@@ -57,7 +60,7 @@ struct electromagnetic_material_params_gpu
     device_vector<double>   bc_coeffs;
 };
 
-struct electromagnetic_field_gpu
+struct field_gpu
 {
     device_vector<double>   Ex;
     device_vector<double>   Ey;
@@ -83,7 +86,50 @@ struct electromagnetic_field_gpu
     void        zero(void);
     void        resize(size_t);
     raw_ptrs    data(void);
-    void        copyin(const electromagnetic_field&);
-    void        copyout(electromagnetic_field&);
+    void        copyin(const field&);
+    void        copyout(field&);
+
+};
+
+struct solver_state
+{
+    std::vector<entity_data_cpu>        eds;
+    field                               emf_curr;
+    field                               emf_next;
+    material_params                     matparams;
+
+    double                              delta_t;
+    double                              curr_time;
+    size_t                              curr_timestep;
+
+    field                               dx;
+    field                               dy;
+    field                               dz;
+};
+
+struct solver_state_gpu
+{
+    std::vector<entity_data_cpu>        eds;
+    std::vector<entity_data_gpu>        edgs;
+    field_gpu                           emf_curr;
+    field_gpu                           emf_next;
+    material_params                     matparams;
+
+    double                              delta_t;
+    double                              curr_time;
+    size_t                              curr_timestep;
+
+    field_gpu                           dx;
+    field_gpu                           dy;
+    field_gpu                           dz;
+};
+
+void init_from_model(const model&, solver_state&);
+void apply_operator(solver_state&, const field&, field&);
+void export_to_silo(const model&, const solver_state&, const std::string&);
+
+void init_from_model(const model&, solver_state_gpu&);
+void apply_operator(solver_state_gpu&, const field_gpu&, field_gpu&);
+void export_to_silo(const model&, const field&, const std::string&);
 
-};
\ No newline at end of file
+} // namespace maxwell
\ No newline at end of file
diff --git a/src/maxwell_interface.cpp b/src/maxwell_interface.cpp
index 76b732ca6389bff7943f8edbbf8a4f1e95c115f0..a26f26ca83777a9b59a2ecb3b16b20fdce420a9a 100644
--- a/src/maxwell_interface.cpp
+++ b/src/maxwell_interface.cpp
@@ -1,7 +1,9 @@
 #include "maxwell_interface.h"
 
+namespace maxwell {
+
 void
-electromagnetic_field::resize(size_t p_num_dofs)
+field::resize(size_t p_num_dofs)
 {
     Ex = vecxd::Zero(p_num_dofs);
     Ey = vecxd::Zero(p_num_dofs);
@@ -13,7 +15,7 @@ electromagnetic_field::resize(size_t p_num_dofs)
 }
 
 void
-electromagnetic_field_gpu::zero()
+field_gpu::zero()
 {
     Ex.zero();
     Ey.zero();
@@ -24,7 +26,7 @@ electromagnetic_field_gpu::zero()
 }
 
 void
-electromagnetic_field_gpu::resize(size_t p_num_dofs)
+field_gpu::resize(size_t p_num_dofs)
 {
     Ex.resize(p_num_dofs);
     Ey.resize(p_num_dofs);
@@ -35,8 +37,8 @@ electromagnetic_field_gpu::resize(size_t p_num_dofs)
     num_dofs = p_num_dofs;
 }
 
-electromagnetic_field_gpu::raw_ptrs
-electromagnetic_field_gpu::data(void)
+field_gpu::raw_ptrs
+field_gpu::data(void)
 {
     raw_ptrs ret;
 
@@ -52,7 +54,7 @@ electromagnetic_field_gpu::data(void)
 }
 
 void
-electromagnetic_field_gpu::copyin(const electromagnetic_field& emf)
+field_gpu::copyin(const field& emf)
 {
     Ex.copyin(emf.Ex.data(), emf.Ex.size());
     Ey.copyin(emf.Ey.data(), emf.Ey.size());
@@ -63,7 +65,7 @@ electromagnetic_field_gpu::copyin(const electromagnetic_field& emf)
 }
 
 void
-electromagnetic_field_gpu::copyout(electromagnetic_field& emf)
+field_gpu::copyout(field& emf)
 {
     if (num_dofs != emf.num_dofs)
         emf.resize(num_dofs);
@@ -74,4 +76,6 @@ electromagnetic_field_gpu::copyout(electromagnetic_field& emf)
     Hx.copyout(emf.Hx.data());
     Hy.copyout(emf.Hy.data());
     Hz.copyout(emf.Hz.data());
-}
\ No newline at end of file
+}
+
+} // namespace maxwell
diff --git a/src/maxwell_solver.cpp b/src/maxwell_solver.cpp
index ca3b15b78c7cc7012948a9a623131214b0ca3350..5f65394c1be8422eb38eaf2144d55fe8e3180ac8 100644
--- a/src/maxwell_solver.cpp
+++ b/src/maxwell_solver.cpp
@@ -1,10 +1,6 @@
-#include <iostream>
-
-#include "gmsh_io.h"
-#include "maxwell_interface.h"
-#include "kernels_cpu.h"
-#include "kernels_gpu.h"
+#include "gmsh.h"
 #include "silo_output.hpp"
+#include "maxwell_interface.h"
 
 static void
 make_geometry(int order, double mesh_h)
@@ -32,267 +28,6 @@ make_geometry(int order, double mesh_h)
     gmm::setSize(vp, mesh_h);
 }
 
-struct solver_state
-{
-    std::vector<entity_data_cpu>        eds;
-    electromagnetic_field               emf_curr;
-    electromagnetic_field               emf_next;
-    electromagnetic_material_params     matparams;
-
-    double                              delta_t;
-    double                              curr_time;
-    size_t                              curr_timestep;
-
-    electromagnetic_field               dx;
-    electromagnetic_field               dy;
-    electromagnetic_field               dz;
-};
-
-struct solver_state_gpu
-{
-    std::vector<entity_data_cpu>        eds;
-    std::vector<entity_data_gpu>        edgs;
-    electromagnetic_field_gpu           emf_curr;
-    electromagnetic_field_gpu           emf_next;
-    electromagnetic_material_params     matparams;
-
-    double                              delta_t;
-    double                              curr_time;
-    size_t                              curr_timestep;
-
-    electromagnetic_field_gpu           dx;
-    electromagnetic_field_gpu           dy;
-    electromagnetic_field_gpu           dz;
-};
-
-void init_from_model(const model& mod, solver_state& state)
-{
-    state.emf_curr.resize( mod.num_dofs() );
-    state.emf_next.resize( mod.num_dofs() );
-    state.dx.resize( mod.num_dofs() );
-    state.dy.resize( mod.num_dofs() );
-    state.dz.resize( mod.num_dofs() );
-
-    auto E = [](const point_3d& pt) -> vec3d {
-        vec3d ret;
-        ret(0) = std::sin(M_PI * pt.y());
-        ret(1) = 0;
-        ret(2) = 0;
-        return ret;
-    };
-
-    for (auto& e : mod)
-    {
-        entity_data_cpu ed;
-        e.populate_entity_data(ed);
-        state.eds.push_back( std::move(ed) );
-        e.project(E, state.emf_curr.Ex, state.emf_curr.Ey, state.emf_curr.Ez);
-    }
-}
-
-void init_from_model(const model& mod, solver_state_gpu& state)
-{
-    state.emf_curr.resize( mod.num_dofs() );
-    state.emf_next.resize( mod.num_dofs() );
-    state.dx.resize( mod.num_dofs() );
-    state.dy.resize( mod.num_dofs() );
-    state.dz.resize( mod.num_dofs() );
-
-    electromagnetic_field emf_io;
-    emf_io.resize( mod.num_dofs() );
-
-    auto E = [](const point_3d& pt) -> vec3d {
-        vec3d ret;
-        ret(0) = std::sin(M_PI * pt.y());
-        ret(1) = 0;
-        ret(2) = 0;
-        return ret;
-    };
-
-
-    for (auto& e : mod)
-    {
-        entity_data_cpu ed;
-        e.populate_entity_data(ed);
-        e.project(E, emf_io.Ex, emf_io.Ey, emf_io.Ez);
-        entity_data_gpu edg(ed);
-        state.eds.push_back( std::move(ed) );
-        state.edgs.push_back( std::move(edg) );
-    }
-
-    state.emf_curr.copyin(emf_io);
-}
-
-void compute_curls(solver_state& state, const electromagnetic_field& curr,
-    electromagnetic_field& next)
-{
-    for (const auto& ed : state.eds)
-    {
-        compute_field_derivatives(ed, curr.Ex, state.dx.Ex, state.dy.Ex, state.dz.Ex);
-        compute_field_derivatives(ed, curr.Ey, state.dx.Ey, state.dy.Ey, state.dz.Ey);
-        compute_field_derivatives(ed, curr.Ez, state.dx.Ez, state.dy.Ez, state.dz.Ez);
-        compute_field_derivatives(ed, curr.Hx, state.dx.Hx, state.dy.Hx, state.dz.Hx);
-        compute_field_derivatives(ed, curr.Hy, state.dx.Hy, state.dy.Hy, state.dz.Hy);
-        compute_field_derivatives(ed, curr.Hz, state.dx.Hz, state.dy.Hz, state.dz.Hz);
-    }
-
-    next.Ex = state.dy.Hz - state.dz.Hy;
-    next.Ey = state.dz.Hx - state.dx.Hz;
-    next.Ez = state.dx.Hy - state.dy.Hx;
-    next.Hx = state.dz.Ey - state.dy.Ez;
-    next.Hy = state.dx.Ez - state.dz.Ex;
-    next.Hz = state.dy.Ex - state.dx.Ey;
-}
-
-void compute_curls(solver_state_gpu& state, const electromagnetic_field_gpu& curr,
-    electromagnetic_field_gpu& next)
-{
-    auto currp = curr.data();
-    auto nextp = next.data();
-    auto dxp = state.dx.data();
-    auto dyp = state.dy.data();
-    auto dzp = state.dz.data();
-
-    for (const auto& edg : state.edgs)
-    {
-        gpu_compute_field_derivatives(edg, currp.Ex, dxp.Ex, dyp.Ex, dzp.Ex, 1.0);
-        gpu_compute_field_derivatives(edg, currp.Ey, dxp.Ey, dyp.Ey, dzp.Ey, 1.0);
-        gpu_compute_field_derivatives(edg, currp.Ez, dxp.Ez, dyp.Ez, dzp.Ez, 1.0);
-        gpu_compute_field_derivatives(edg, currp.Hx, dxp.Hx, dyp.Hx, dzp.Hx, 1.0);
-        gpu_compute_field_derivatives(edg, currp.Hy, dxp.Hy, dyp.Hy, dzp.Hy, 1.0);
-        gpu_compute_field_derivatives(edg, currp.Hz, dxp.Hz, dyp.Hz, dzp.Hz, 1.0);
-    }
-
-    
-    gpu_subtract(nextp.Ex, dyp.Hz, dzp.Hy, nextp.num_dofs);
-    gpu_subtract(nextp.Ey, dzp.Hx, dxp.Hz, nextp.num_dofs);
-    gpu_subtract(nextp.Ez, dxp.Hy, dyp.Hx, nextp.num_dofs);
-    gpu_subtract(nextp.Hx, dzp.Ey, dyp.Ez, nextp.num_dofs);
-    gpu_subtract(nextp.Hy, dxp.Ez, dzp.Ex, nextp.num_dofs);
-    gpu_subtract(nextp.Hz, dyp.Ex, dxp.Ey, nextp.num_dofs);
-}
-
-void apply_operator(solver_state& state, const electromagnetic_field& curr,
-    electromagnetic_field& next)
-{
-    compute_curls(state, curr, next);
-}
-
-void apply_operator(solver_state_gpu& state, const electromagnetic_field_gpu& curr,
-    electromagnetic_field_gpu& next)
-{
-    compute_curls(state, curr, next);
-}
-
-void
-export_to_silo(const model& mod, const solver_state& state, const std::string& filename)
-{
-    silo sdb;
-    sdb.create_db(filename);
-    sdb.import_mesh_from_gmsh();
-    sdb.write_mesh();
-
-    std::vector<double> curr_Ex; curr_Ex.resize( mod.num_cells() );
-    std::vector<double> curr_Ey; curr_Ey.resize( mod.num_cells() );
-    std::vector<double> curr_Ez; curr_Ez.resize( mod.num_cells() );
-    std::vector<double> curr_Hx; curr_Hx.resize( mod.num_cells() );
-    std::vector<double> curr_Hy; curr_Hy.resize( mod.num_cells() );
-    std::vector<double> curr_Hz; curr_Hz.resize( mod.num_cells() );
-
-    std::vector<double> next_Ex; next_Ex.resize( mod.num_cells() );
-    std::vector<double> next_Ey; next_Ey.resize( mod.num_cells() );
-    std::vector<double> next_Ez; next_Ez.resize( mod.num_cells() );
-    std::vector<double> next_Hx; next_Hx.resize( mod.num_cells() );
-    std::vector<double> next_Hy; next_Hy.resize( mod.num_cells() );
-    std::vector<double> next_Hz; next_Hz.resize( mod.num_cells() );
-
-    for (size_t iE = 0; iE < mod.num_entities(); iE++)
-    {
-        auto& e = mod.entity_at(iE);
-        for (size_t iT = 0; iT < e.num_cells(); iT++)
-        {
-            auto& pe = e.cell(iT);
-            auto& re = e.cell_refelem(pe);
-            vecxd phi_bar = re.basis_functions({1./3., 1./3., 1./3.});
-            auto num_bf = re.num_basis_functions();
-            auto ofs = e.cell_global_dof_offset(iT);
-            auto gi = e.cell_global_index_by_gmsh(iT);
-            curr_Ex[gi] = state.emf_curr.Ex.segment(ofs, num_bf).dot(phi_bar);
-            curr_Ey[gi] = state.emf_curr.Ey.segment(ofs, num_bf).dot(phi_bar);
-            curr_Ez[gi] = state.emf_curr.Ez.segment(ofs, num_bf).dot(phi_bar);
-            curr_Hx[gi] = state.emf_curr.Hx.segment(ofs, num_bf).dot(phi_bar);
-            curr_Hy[gi] = state.emf_curr.Hy.segment(ofs, num_bf).dot(phi_bar);
-            curr_Hz[gi] = state.emf_curr.Hz.segment(ofs, num_bf).dot(phi_bar);
-            next_Ex[gi] = state.emf_next.Ex.segment(ofs, num_bf).dot(phi_bar);
-            next_Ey[gi] = state.emf_next.Ey.segment(ofs, num_bf).dot(phi_bar);
-            next_Ez[gi] = state.emf_next.Ez.segment(ofs, num_bf).dot(phi_bar);
-            next_Hx[gi] = state.emf_next.Hx.segment(ofs, num_bf).dot(phi_bar);
-            next_Hy[gi] = state.emf_next.Hy.segment(ofs, num_bf).dot(phi_bar);
-            next_Hz[gi] = state.emf_next.Hz.segment(ofs, num_bf).dot(phi_bar);
-        }
-    }
-
-    sdb.write_zonal_variable("curr_Ex", curr_Ex);
-    sdb.write_zonal_variable("curr_Ey", curr_Ey);
-    sdb.write_zonal_variable("curr_Ez", curr_Ez);
-    sdb.write_zonal_variable("curr_Hx", curr_Hx);
-    sdb.write_zonal_variable("curr_Hy", curr_Hy);
-    sdb.write_zonal_variable("curr_Hz", curr_Hz);
-    //sdb.write_vector_expression("curr_E", "{curr_Ex, curr_Ey, curr_Ez}");
-    //sdb.write_vector_expression("curr_H", "{curr_Hx, curr_Hy, curr_Hz}");
-    sdb.write_zonal_variable("next_Ex", next_Ex);
-    sdb.write_zonal_variable("next_Ey", next_Ey);
-    sdb.write_zonal_variable("next_Ez", next_Ez);
-    sdb.write_zonal_variable("next_Hx", next_Hx);
-    sdb.write_zonal_variable("next_Hy", next_Hy);
-    sdb.write_zonal_variable("next_Hz", next_Hz);
-    //sdb.write_vector_expression("next_E", "{next_Ex, next_Ey, next_Ez}");
-    //sdb.write_vector_expression("next_H", "{next_Hx, next_Hy, next_Hz}");
-}
-
-void
-export_to_silo(const model& mod, const electromagnetic_field& emf,
-    const std::string& filename)
-{
-    silo sdb;
-    sdb.create_db(filename);
-    sdb.import_mesh_from_gmsh();
-    sdb.write_mesh();
-
-    std::vector<double> Ex; Ex.resize( mod.num_cells() );
-    std::vector<double> Ey; Ey.resize( mod.num_cells() );
-    std::vector<double> Ez; Ez.resize( mod.num_cells() );
-    std::vector<double> Hx; Hx.resize( mod.num_cells() );
-    std::vector<double> Hy; Hy.resize( mod.num_cells() );
-    std::vector<double> Hz; Hz.resize( mod.num_cells() );
-
-    for (size_t iE = 0; iE < mod.num_entities(); iE++)
-    {
-        auto& e = mod.entity_at(iE);
-        for (size_t iT = 0; iT < e.num_cells(); iT++)
-        {
-            auto& pe = e.cell(iT);
-            auto& re = e.cell_refelem(pe);
-            vecxd phi_bar = re.basis_functions({1./3., 1./3., 1./3.});
-            auto num_bf = re.num_basis_functions();
-            auto ofs = e.cell_global_dof_offset(iT);
-            auto gi = e.cell_global_index_by_gmsh(iT);
-            Ex[gi] = emf.Ex.segment(ofs, num_bf).dot(phi_bar);
-            Ey[gi] = emf.Ey.segment(ofs, num_bf).dot(phi_bar);
-            Ez[gi] = emf.Ez.segment(ofs, num_bf).dot(phi_bar);
-            Hx[gi] = emf.Hx.segment(ofs, num_bf).dot(phi_bar);
-            Hy[gi] = emf.Hy.segment(ofs, num_bf).dot(phi_bar);
-            Hz[gi] = emf.Hz.segment(ofs, num_bf).dot(phi_bar);
-        }
-    }
-
-    sdb.write_zonal_variable("Ex", Ex);
-    sdb.write_zonal_variable("Ey", Ey);
-    sdb.write_zonal_variable("Ez", Ez);
-    sdb.write_zonal_variable("Hx", Hx);
-    sdb.write_zonal_variable("Hy", Hy);
-    sdb.write_zonal_variable("Hz", Hz);
-}
 
 int main(int argc, const char *argv[])
 {
@@ -301,15 +36,15 @@ int main(int argc, const char *argv[])
     make_geometry(1, 0.05);
 
     model mod(1,2);
-    solver_state_gpu state;
-    init_from_model(mod, state);
-
-    apply_operator(state, state.emf_curr, state.emf_next);
+    
+    maxwell::solver_state_gpu state;
+    maxwell::init_from_model(mod, state);
+    maxwell::apply_operator(state, state.emf_curr, state.emf_next);
 
-    electromagnetic_field res;
+    maxwell::field res;
     state.emf_next.copyout(res);
 
-    export_to_silo(mod, res, "test.silo");
+    maxwell::export_to_silo(mod, res, "test.silo");
 
     //gmsh::finalize();