diff --git a/src/pystencils_autodiff/CMakeLists.tmpl.txt b/src/pystencils_autodiff/CMakeLists.tmpl.txt
index 6f9bcf59ed79ecf85e5cd6ca5073ff68f0f82a94..3765fab75bc575e1789d564d0ba06a5bb3ac97ae 100644
--- a/src/pystencils_autodiff/CMakeLists.tmpl.txt
+++ b/src/pystencils_autodiff/CMakeLists.tmpl.txt
@@ -1,6 +1,6 @@
 waLBerla_link_files_to_builddir( *.prm )
-waLBerla_add_executable ( NAME autogen
+waLBerla_add_executable ( NAME {{ cmake_target_name }}
                           FILES {%- for f in files_to_compile %}
                                 {{ f }} 
                                 {%- endfor %}
-                          DEPENDS blockforest core cuda field lbm geometry timeloop gui )
+                          DEPENDS {{depends | join(' ')}})
diff --git a/src/pystencils_autodiff/_backport.py b/src/pystencils_autodiff/_backport.py
index c49103ab7c0f21ca5b08cf150671dc4a9ebe52ef..e9ba2790bac9a743e3175a464526a579eb878b00 100644
--- a/src/pystencils_autodiff/_backport.py
+++ b/src/pystencils_autodiff/_backport.py
@@ -10,6 +10,8 @@
 
 import itertools
 
+import sympy as sp
+
 import pystencils
 from pystencils.astnodes import KernelFunction, ResolvedFieldAccess, SympyAssignment
 from pystencils.interpolation_astnodes import InterpolatorAccess
@@ -42,6 +44,7 @@ def compatibility_hacks():
     pystencils.fields = fields
     KernelFunction.fields_read = property(fields_read)
     KernelFunction.fields_written = property(fields_written)
+    sp.Expr.undefined_symbols = sp.Expr.free_symbols
 
 
 compatibility_hacks()
diff --git a/src/pystencils_autodiff/framework_integration/astnodes.py b/src/pystencils_autodiff/framework_integration/astnodes.py
index 3f60bf879f4c38a8eac9123a425c5246bc7f8a92..d83b6bd2984d141536e696d04267e948da8d8be4 100644
--- a/src/pystencils_autodiff/framework_integration/astnodes.py
+++ b/src/pystencils_autodiff/framework_integration/astnodes.py
@@ -164,6 +164,7 @@ def generate_kernel_call(kernel_function):
 
 class JinjaCppFile(Node):
     TEMPLATE: jinja2.Template = None
+    NOT_PRINT_TYPES = (pystencils.Field, pystencils.TypedSymbol, bool)
 
     def __init__(self, ast_dict):
         self.ast_dict = pystencils.utils.DotDict(ast_dict)
@@ -174,18 +175,23 @@ class JinjaCppFile(Node):
     def args(self):
         """Returns all arguments/children of this node."""
         ast_nodes = [a for a in self.ast_dict.values() if isinstance(a, (Node, str))]
-        iterables_of_ast_nodes = [a for a in self.ast_dict.values() if isinstance(a, Iterable)]
+        iterables_of_ast_nodes = [a for a in self.ast_dict.values() if isinstance(a, Iterable)
+                                  and not isinstance(a, str)]
         return ast_nodes + list(itertools.chain.from_iterable(iterables_of_ast_nodes))
 
     @property
     def symbols_defined(self):
         """Set of symbols which are defined by this node."""
-        return set()
+        return set(itertools.chain.from_iterable(a.symbols_defined
+                                                 for a in self.args
+                                                 if hasattr(a, 'symbols_defined')))
 
     @property
     def undefined_symbols(self):
         """Symbols which are used but are not defined inside this node."""
-        return set()
+        return set(itertools.chain.from_iterable(a.undefined_symbols
+                                                 for a in self.args
+                                                 if hasattr(a, 'undefined_symbols'))) - self.symbols_defined
 
     def _print(self, node):
         if isinstance(node, Node):
@@ -210,11 +216,11 @@ class JinjaCppFile(Node):
     def __str__(self):
         assert self.TEMPLATE, f"Template of {self.__class__} must be set"
         render_dict = {k: (self._print(v)
-                           if not isinstance(v, (pystencils.Field, pystencils.TypedSymbol, bool)) and v is not None
+                           if not isinstance(v, self.NOT_PRINT_TYPES) and v is not None
                            else v)
                        if not isinstance(v, Iterable) or isinstance(v, str)
                        else [(self._print(a)
-                              if not isinstance(a, (pystencils.Field, pystencils.TypedSymbol, bool)) and a is not None
+                              if not isinstance(a, self.NOT_PRINT_TYPES) and a is not None
                               else a)
                              for a in v]
                        for k, v in self.ast_dict.items()}
diff --git a/src/pystencils_autodiff/simulation.py b/src/pystencils_autodiff/simulation.py
index 9cc7c682eb8ff00208bc171e3a2a4f34c07b2dfd..8b89e334835758cd15106bcf6ddb41324c3b837d 100644
--- a/src/pystencils_autodiff/simulation.py
+++ b/src/pystencils_autodiff/simulation.py
@@ -16,15 +16,42 @@ import lbmpy_walberla
 import pystencils
 import pystencils_walberla.codegen
 from pystencils.astnodes import Block, EmptyLine
+from pystencils.cpu.cpujit import get_headers
 from pystencils_autodiff.walberla import (
     AllocateAllFields, CMakeLists, Communication, DefineKernelObjects, DefinitionsHeader, FieldCopy,
     ForLoop, InitBoundaryHandling, LbCommunicationSetup, ResolveUndefinedSymbols, SwapFields,
     SweepCreation, SweepOverAllBlocks, UniformBlockforestFromConfig, WalberlaMain, WalberlaModule)
 
+WALBERLA_MODULES = ["blockforest",
+                    "boundary",
+                    "communication",
+                    "core",
+                    "cuda",
+                    "domain_decomposition",
+                    "executiontree",
+                    "fft",
+                    "field",
+                    "gather",
+                    "geometry",
+                    "gui",
+                    "lbm",
+                    "mesa_pd",
+                    "mesh",
+                    "pde",
+                    "pe",
+                    "pe_coupling",
+                    "postprocessing",
+                    "python_coupling",
+                    "simd",
+                    "sqlite",
+                    "stencil",
+                    "timeloop",
+                    "vtk",
+                    "walberla_openvdb"]
+
 
 class Simulation():
     def _get_sweep_class_name(prefix='Kernel'):
-
         ctr = 0
         while True:
             yield f'{prefix}{ctr}'
@@ -36,7 +63,8 @@ class Simulation():
                  boundary_handling: pystencils.boundaries.BoundaryHandling = None,
                  lb_rule=None,
                  refinement_scaling=None,
-                 boundary_handling_target='gpu'):
+                 boundary_handling_target='gpu',
+                 cmake_target_name='autogen_app'):
         self._data_handling = graph_data_handling
         self._lb_rule = lb_rule
         self._refinement_scaling = refinement_scaling
@@ -53,6 +81,7 @@ class Simulation():
         self._boundary_handling_target = boundary_handling_target
         self._data_handling.merge_swaps_with_kernel_calls()
         self._packinfo_class = 'PackInfo'
+        self.cmake_target_name = cmake_target_name
 
     def _create_helper_files(self) -> Dict[str, str]:
 
@@ -109,11 +138,11 @@ class Simulation():
                 Block([
                     field_allocations,
                     InitBoundaryHandling(self._block_forest.blocks,
-                                             flag_field_id,
-                                             pdf_field_id,
-                                             self.boundary_conditions,
-                                             self._boundary_kernels,
-                                             self._field_allocations)
+                                         flag_field_id,
+                                         pdf_field_id,
+                                         self.boundary_conditions,
+                                         self._boundary_kernels,
+                                         self._field_allocations)
                     if self._boundary_handling else EmptyLine(),
                     LbCommunicationSetup(self._lb_model_name,
                                          pdf_field_id,
@@ -126,27 +155,47 @@ class Simulation():
                 ]), self.parameter_config_block
             )
         ])))
+        from pystencils_autodiff.framework_integration.printer import DebugFrameworkPrinter
+
+        module.printer = DebugFrameworkPrinter()
 
         self._codegen_context.write_file("main.cpp", str(module))
         return module
 
     def _create_defintions_header(self):
         self._codegen_context.write_file("UserDefinitions.h",
-                                         str(DefinitionsHeader(self._lb_model_name, self._flag_field_dtype)))
-
-    def _create_cmake_file(self):
+                                         str(DefinitionsHeader(self._lb_model_name if self._lb_rule else None,
+                                                               self._flag_field_dtype)))
+
+    def _create_cmake_file(self, extra_dependencis=[]):
+        walberla_dependencies = []
+        import re
+        regex = re.compile(r'^["<](\w*)/.*[>$"]')
+        headers = get_headers(self._module)
+        for h in headers:
+            match = regex.match(h)
+            if match:
+                module = match[1]
+                if module in WALBERLA_MODULES:
+                    walberla_dependencies.append(module)
+
+        dependencies = walberla_dependencies + extra_dependencis
         try:
             self._codegen_context.write_file("CMakeLists.txt",
-                                             str(CMakeLists([f for f in self._codegen_context.files_written()
-                                                             if f.endswith('.cpp') or f.endswith('.cu')])))
+                                             str(CMakeLists(self.cmake_target_name,
+                                                            [f for f in self._codegen_context.files_written()
+                                                             if f.endswith('.cpp') or f.endswith('.cu')],
+                                                            depends=dependencies)))
         except AttributeError:
             self._codegen_context.write_file("CMakeLists.txt",
-                                             str(CMakeLists([f for f in self._codegen_context.files.keys()
-                                                             if f.endswith('.cpp') or f.endswith('.cu')])))
+                                             str(CMakeLists(self.cmake_target_name,
+                                                            [f for f in self._codegen_context.files.keys()
+                                                             if f.endswith('.cpp') or f.endswith('.cu')],
+                                                            depends=dependencies)))
 
     def write_files(self):
         self._create_helper_files()
-        self._create_module()
+        self._module = self._create_module()
         self._create_defintions_header()
         self._create_cmake_file()  # has to be called last
 
diff --git a/src/pystencils_autodiff/walberla.py b/src/pystencils_autodiff/walberla.py
index 005e4e334f6dfa9a21717ff82e056e0c8f702a60..e882ce8b1dc8fdb9ee31e9ba85e3d78dc05a152d 100644
--- a/src/pystencils_autodiff/walberla.py
+++ b/src/pystencils_autodiff/walberla.py
@@ -62,8 +62,10 @@ class WalberlaModule(JinjaCppFile):
 class CMakeLists(JinjaCppFile):
     TEMPLATE = read_template_from_file(join(dirname(__file__), 'CMakeLists.tmpl.txt'))
 
-    def __init__(self, files_to_compile):
-        ast_dict = {'files_to_compile': files_to_compile}  # we try to avoid evil globbing
+    def __init__(self, cmake_target_name, files_to_compile, depends):
+        ast_dict = {'files_to_compile': files_to_compile,  # we try to avoid evil globbing
+                    'cmake_target_name': cmake_target_name,
+                    'depends': depends}
         JinjaCppFile.__init__(self, ast_dict)
 
 
@@ -138,7 +140,9 @@ class DefinitionsHeader(JinjaCppFile):
     TEMPLATE = read_template_from_file(join(dirname(__file__), 'walberla_user_defintions.tmpl.hpp'))
 
     def __init__(self, lb_model_name, flag_field_type):
-        self.headers = ['<cstdint>', '"lbm/field/PdfField.h"', f'"{lb_model_name}.h"', '"field/FlagField.h"']
+        self.headers = ['<cstdint>',  '"field/FlagField.h"']
+        if lb_model_name:
+            self.headers.append(f'"{lb_model_name}.h"')
         super().__init__({'lb_model_name': lb_model_name, 'flag_field_type': flag_field_type})
 
 
@@ -256,12 +260,11 @@ class FlagFieldAllocation(FieldAllocation):
 
 
 class WalberlaVector(JinjaCppFile):
-    TEMPLATE = jinja2.Template("""math::Vector{{ndim}}<{{dtype}}>({{offsets}})""")
+    TEMPLATE = jinja2.Template("""math::Vector{{ndim}}<{{dtype}}>({{offset | join(', ')}})""")
 
     def __init__(self, offset, dtype='real_t'):
         ast_dict = {
             'offset': offset,
-            'offsets': ', '.join(str(o) for o in offset),
             'dtype': dtype,
             'ndim': len(offset),
         }
@@ -553,17 +556,20 @@ timeloop.run();
 
 class CppObjectConstruction(JinjaCppFile, pystencils.astnodes.SympyAssignment):
 
-    TEMPLATE = jinja2.Template("""{{  symbol.dtype  }} {{ symbol }}({{ arg_list }});""")  # noqa
+    TEMPLATE = jinja2.Template("""{{  symbol.dtype  }} {{ symbol }}({{ args | join(', ') }});""")  # noqa
 
     def __init__(self, symbol, args):
         JinjaCppFile.__init__(self, {})
         self.ast_dict.update({
             'symbol': symbol,
             'args': args,
-            'arg_list': ', '.join(self.printer(a) for a in args)
         })
         pystencils.astnodes.SympyAssignment.__init__(self, symbol, 1)
 
+    @property
+    def symbol(self):
+        return self.lhs
+
 
 class ResolveUndefinedSymbols(JinjaCppFile):
 
@@ -658,10 +664,12 @@ class AllocateAllFields(JinjaCppFile):
 {% for c in cpu_arrays %}
 {{- c }}
 {% endfor %}
+{%- if gpu_arrays -%}
 // GPU Arrays
 {% for g in gpu_arrays %}
 {{- g }}
 {% endfor %}
+{%- endif %}
 {{ block  }}
 """)  # noqa
 
@@ -747,7 +755,7 @@ class InitBoundaryHandling(JinjaCppFile):
 
 
 class GeneratedBoundaryInitialization(JinjaCppFile):
-    TEMPLATE = jinja2.Template("""lbm::{{ boundary_condition }} {{ identifier }}( {{ block_forest }}, {{ pdf_field_id }}{{ parameter_str }} );
+    TEMPLATE = jinja2.Template("""lbm::{{ boundary_condition }} {{ identifier }}( {{ block_forest }}, {{ parameter_ids | join(', ') }} );
 {{ identifier }}.fillFromFlagField<FlagField_T>( {{ block_forest }}, {{ flag_field_id }}, FlagUID("{{ boundary_condition }}"), {{ fluid_uid }} );
 """)  # noqa
 
@@ -771,9 +779,6 @@ class GeneratedBoundaryInitialization(JinjaCppFile):
                          for p in parameters
                          if (p.is_field_pointer or not p.is_field_parameter) and
                          p.symbol.name not in ('_data_indexVector', '_data_pdfs', 'indexVectorSize',)]
-        parameter_str = ', '.join(p.name for p in parameter_ids)
-        if parameter_str:
-            parameter_str = ', ' + parameter_str
 
         self.fluid = FlagUidDefinition("fluid")
         ast_dict = {'block_forest': block_forest,
@@ -782,8 +787,7 @@ class GeneratedBoundaryInitialization(JinjaCppFile):
                     'pdf_field_id': pdf_field_id,
                     'fluid_uid': fluid_uid,
                     'flag_field_id': flag_field_id,
-                    'parameter_ids': parameter_ids,
-                    'parameter_str': parameter_str
+                    'parameter_ids': [pdf_field_id] + parameter_ids,
                     }
         super().__init__(ast_dict)
 
@@ -803,13 +807,17 @@ class GeneratedBoundaryInitialization(JinjaCppFile):
 
 
 class SweepCreation(JinjaCppFile):
-    TEMPLATE = jinja2.Template("""{{ sweep_class_name }}( {{ parameter_str }} )""")  # noqa
+    TEMPLATE = jinja2.Template("""{{ sweep_class_name }}( {{ parameter_ids | join(', ') }} )""")  # noqa
 
     def __init__(self,
                  sweep_class_name: str,
                  field_allocation: AllocateAllFields,
                  ast,
                  parameters_to_ignore=[]):
+
+        import pystencils_walberla.jinja_filters
+        parameters_to_ignore += pystencils_walberla.jinja_filters.SPECIAL_SYMBOL_NAMES
+
         def resolve_parameter(p):
             if ast.target == 'cpu':
                 dict = field_allocation._cpu_allocations
@@ -822,11 +830,11 @@ class SweepCreation(JinjaCppFile):
         parameter_ids = [resolve_parameter(p)
                          for p in parameters
                          if (p.is_field_pointer and p.symbol.field_name not in parameters_to_ignore)
-                         or not p.is_field_parameter]
+                         or not p.is_field_parameter and p.symbol.name not in parameters_to_ignore]
 
         ast_dict = {'sweep_class_name': sweep_class_name,
                     'parameter_ids': parameter_ids,
-                    'parameter_str': ', '.join(p.name for p in parameter_ids)}
+                    }
         super().__init__(ast_dict)
         self.symbol = TypedSymbol(self.ast_dict.sweep_class_name.lower(), self.ast_dict.sweep_class_name)
 
@@ -838,6 +846,8 @@ class SweepCreation(JinjaCppFile):
     def undefined_symbols(self):
         return set(self.ast_dict.parameter_ids)
 
+    free_symbols = undefined_symbols
+
     @property
     def symbols_defined(self):
         return {self.symbol}
@@ -918,3 +928,27 @@ class Communication(JinjaCppFile):
         super().__init__(ast_dict)
 
     headers = ["<algorithm>"]
+
+
+class VdbWriter(CppObjectConstruction):
+    def __init__(self, block_forest):
+        symbol = TypedSymbol('vdb', 'walberla_openvdb::OpenVdbFieldWriter')
+        super().__init__(symbol, block_forest)
+
+    headers = ['"walberla_openvdb/OpenVdbFieldWriter.h"']
+
+
+class WriteVdb(SweepOverAllBlocks):
+    TEMPLATE = jinja2.Template(
+        """{{vdb_writer}}.beginFile({{file_prefix}}{% if time %}, optional<float>({{time}}){% endif %});
+{% for f in fields %}
+{{ vdb_writer }}.addField<{{ f.dtype }}, {{ f.vdb_type }}>( {{f.id}}, {{f.name}} );
+{% endfor %}
+{{vdb_writer}}.writeFile();""")
+
+    def __init__(self, fields_to_write, block_forest):
+        functor = VdbWriter(block_forest)
+        ast_dict = {'functor': functor,
+                    'vdb_writer': functor.symbol,
+                    'block_forest': block_forest}
+        super().__init__(ast_dict)
diff --git a/src/pystencils_autodiff/walberla_user_defintions.tmpl.hpp b/src/pystencils_autodiff/walberla_user_defintions.tmpl.hpp
index 0df3276c1d924ade412a667fe6a39bb18e3d53d8..278e8fef9f1d5ce2fdc0361dafe15737c8cd1361 100644
--- a/src/pystencils_autodiff/walberla_user_defintions.tmpl.hpp
+++ b/src/pystencils_autodiff/walberla_user_defintions.tmpl.hpp
@@ -14,14 +14,12 @@
 
 namespace walberla_user {
 using namespace walberla;
+{% if with_lbm %}
 using LatticeModel_T = lbm::{{ lb_model_name }};
 using Stencil_T = LatticeModel_T::Stencil;
 using CommunicationStencil_T = LatticeModel_T::CommunicationStencil;
-using PdfField_T = lbm::PdfField<LatticeModel_T>;
+{% endif %}
 using flag_t = {{ flag_field_type }};
 using FlagField_T = FlagField<flag_t>;
 
-using PdfField_T = lbm::PdfField<LatticeModel_T>;
-using VectorField_T = GhostLayerField<real_t, LatticeModel_T::Stencil::D>;
-using ScalarField_T = GhostLayerField<real_t, 1>;
 } // namespace walberla_user
diff --git a/tests/test_walberla.py b/tests/test_walberla.py
index 6dcbabeb4db9157461aa7721a85b51dbaa72e68b..dc8ad0ca93c864db3eebc3bcce18fcb7e5c94dda 100644
--- a/tests/test_walberla.py
+++ b/tests/test_walberla.py
@@ -83,7 +83,8 @@ def test_wald_wiesen_lbm():
         sim = Simulation(lbm_step.data_handling,
                          ctx,
                          lbm_step.boundary_handling,
-                         create_lb_collision_rule(lbm_step.method, optimization=opt_params))
+                         create_lb_collision_rule(lbm_step.method, optimization=opt_params),
+                         cmake_target_name='autogen')
         sim.write_files()
 
         dir = '/localhome/seitz_local/projects/walberla/apps/autogen/'
@@ -98,15 +99,15 @@ def test_global_idx():
     with ManualCodeGenerationContext() as ctx:
         from pystencils_walberla.special_symbols import current_global_idx, aabb_min_x
 
-        dh = GraphDataHandling((20, 30))
+        dh = GraphDataHandling((20, 30, 40))
         my_array = dh.add_array('my_array')
 
-        ast = pystencils.create_kernel([pystencils.Assignment(my_array.center, sum(current_global_idx))]).compile()
-        dh.run_kernel(ast, simulate_only=True)
         ast = pystencils.create_kernel([pystencils.Assignment(my_array.center, aabb_min_x)]).compile()
         dh.run_kernel(ast, simulate_only=True)
+        # ast = pystencils.create_kernel([pystencils.Assignment(my_array.center, sum(current_global_idx))]).compile()
+        # dh.run_kernel(ast, simulate_only=True)
 
-        sim = Simulation(dh, ctx)
+        sim = Simulation(dh, ctx, cmake_target_name='foo')
         sim.write_files()
 
         dir = '/localhome/seitz_local/projects/walberla/apps/foo/'