diff --git a/astnodes.py b/astnodes.py
index 7a1c8198bce798a7e406961f62c964006c5a49a2..6a7026c6a49365a06c0509f486ba9b0c1e2a4a00 100644
--- a/astnodes.py
+++ b/astnodes.py
@@ -2,8 +2,9 @@ import sympy as sp
 from sympy.tensor import IndexedBase
 from pystencils.field import Field
 from pystencils.data_types import TypedSymbol, create_type, cast_func
+from pystencils.kernelparameters import FieldStrideSymbol, FieldPointerSymbol, FieldShapeSymbol
 from pystencils.sympyextensions import fast_subs
-from typing import List, Set, Optional, Union, Any
+from typing import List, Set, Optional, Union, Any, Sequence
 
 NodeOrExpr = Union['Node', sp.Expr]
 
@@ -120,62 +121,48 @@ class Conditional(Node):
 
 class KernelFunction(Node):
 
-    class Argument:
-        def __init__(self, name, dtype, symbol, kernel_function_node):
-            from pystencils.transformations import symbol_name_to_variable_name
-            self.name = name
-            self.dtype = dtype
-            self.is_field_ptr_argument = False
-            self.is_field_shape_argument = False
-            self.is_field_stride_argument = False
-            self.is_field_argument = False
-            self.field_name = ""
-            self.coordinate = None
-            self.symbol = symbol
-
-            if name.startswith(Field.DATA_PREFIX):
-                self.is_field_ptr_argument = True
-                self.is_field_argument = True
-                self.field_name = name[len(Field.DATA_PREFIX):]
-            elif name.startswith(Field.SHAPE_PREFIX):
-                self.is_field_shape_argument = True
-                self.is_field_argument = True
-                self.field_name = name[len(Field.SHAPE_PREFIX):]
-            elif name.startswith(Field.STRIDE_PREFIX):
-                self.is_field_stride_argument = True
-                self.is_field_argument = True
-                self.field_name = name[len(Field.STRIDE_PREFIX):]
-
-            self.field = None
-            if self.is_field_argument:
-                field_map = {symbol_name_to_variable_name(f.name): f for f in kernel_function_node.fields_accessed}
-                self.field = field_map[self.field_name]
-
-        def __lt__(self, other):
-            def score(l):
-                if l.is_field_ptr_argument:
-                    return -4
-                elif l.is_field_shape_argument:
-                    return -3
-                elif l.is_field_stride_argument:
-                    return -2
-                return 0
-
-            if score(self) < score(other):
-                return True
-            elif score(self) == score(other):
-                return self.name < other.name
-            else:
-                return False
+    class Parameter:
+        """Function parameter.
+
+        Each undefined symbol in a `KernelFunction` node becomes a parameter to the function.
+        Parameters are either symbols introduced by the user that never occur on the left hand side of an
+        Assignment, or are related to fields/arrays passed to the function.
+
+        A parameter consists of the typed symbol (symbol property). For field related parameters this is a symbol
+        defined in pystencils.kernelparameters.
+        If the parameter is related to one or multiple fields, these fields are referenced in the fields property.
+        """
+        def __init__(self, symbol, fields):
+            self.symbol = symbol  # type: TypedSymbol
+            self.fields = fields  # type: Sequence[Field]
 
         def __repr__(self):
-            return '<{0} {1}>'.format(self.dtype, self.name)
+            return repr(self.symbol)
+
+        @property
+        def is_field_stride(self):
+            return isinstance(self.symbol, FieldStrideSymbol)
+
+        @property
+        def is_field_shape(self):
+            return isinstance(self.symbol, FieldShapeSymbol)
+
+        @property
+        def is_field_pointer(self):
+            return isinstance(self.symbol, FieldPointerSymbol)
+
+        @property
+        def is_field_parameter(self):
+            return self.is_field_pointer or self.is_field_shape or self.is_field_stride
+
+        @property
+        def field_name(self):
+            return self.fields[0].name
 
     def __init__(self, body, ghost_layers=None, function_name="kernel", backend=""):
         super(KernelFunction, self).__init__()
         self._body = body
         body.parent = self
-        self._parameters = None
         self.function_name = function_name
         self._body.parent = self
         self.compile = None
@@ -193,11 +180,6 @@ class KernelFunction(Node):
     def undefined_symbols(self):
         return set()
 
-    @property
-    def parameters(self):
-        self._update_parameters()
-        return self._parameters
-
     @property
     def body(self):
         return self._body
@@ -207,24 +189,37 @@ class KernelFunction(Node):
         return [self._body]
 
     @property
-    def fields_accessed(self):
+    def fields_accessed(self) -> Set['ResolvedFieldAccess']:
         """Set of Field instances: fields which are accessed inside this kernel function"""
         return set(o.field for o in self.atoms(ResolvedFieldAccess))
 
-    def _update_parameters(self):
-        undefined_symbols = self._body.undefined_symbols - self.global_variables
-        self._parameters = [KernelFunction.Argument(s.name, s.dtype, s, self) for s in undefined_symbols]
+    def get_parameters(self) -> Sequence['KernelFunction.Parameter']:
+        """Returns list of parameters for this function.
+
+        This function is expensive, cache the result where possible!
+        """
+        field_map = {f.name: f for f in self.fields_accessed}
+
+        def get_fields(symbol):
+            if hasattr(symbol, 'field_name'):
+                return field_map[symbol.field_name],
+            elif hasattr(symbol, 'field_names'):
+                return tuple(field_map[fn] for fn in symbol.field_names)
+            return ()
 
-        self._parameters.sort()
+        argument_symbols = self._body.undefined_symbols - self.global_variables
+        parameters = [self.Parameter(symbol, get_fields(symbol)) for symbol in argument_symbols]
+        parameters.sort(key=lambda p: p.symbol.name)
+        return parameters
 
     def __str__(self):
-        self._update_parameters()
-        return '{0} {1}({2})\n{3}'.format(type(self).__name__, self.function_name, self.parameters,
+        params = [p.symbol for p in self.get_parameters()]
+        return '{0} {1}({2})\n{3}'.format(type(self).__name__, self.function_name, params,
                                           ("\t" + "\t".join(str(self.body).splitlines(True))))
 
     def __repr__(self):
-        self._update_parameters()
-        return '{0} {1}({2})'.format(type(self).__name__, self.function_name, self.parameters)
+        params = [p.symbol for p in self.get_parameters()]
+        return '{0} {1}({2})'.format(type(self).__name__, self.function_name, params)
 
 
 class Block(Node):
diff --git a/backends/cbackend.py b/backends/cbackend.py
index a32392a0683b99b686f03d5d24cc2e6472bc62f4..40a621c229528f75dc7fcea651497d776004f0f6 100644
--- a/backends/cbackend.py
+++ b/backends/cbackend.py
@@ -124,7 +124,7 @@ class CBackend:
         raise NotImplementedError("CBackend does not support node of type " + str(type(node)))
 
     def _print_KernelFunction(self, node):
-        function_arguments = ["%s %s" % (str(s.dtype), s.name) for s in node.parameters]
+        function_arguments = ["%s %s" % (str(s.symbol.dtype), s.symbol.name) for s in node.get_parameters()]
         func_declaration = "FUNC_PREFIX void %s(%s)" % (node.function_name, ", ".join(function_arguments))
         if self._signatureOnly:
             return func_declaration
diff --git a/backends/dot.py b/backends/dot.py
index 52184a76659708657f4341ab065dc2dda075af14..bfec9cfdb2a7d9a32285b0a967b47980be78f907 100644
--- a/backends/dot.py
+++ b/backends/dot.py
@@ -54,9 +54,10 @@ def __shortened(node):
     if isinstance(node, LoopOverCoordinate):
         return "Loop over dim %d" % (node.coordinate_to_loop_over,)
     elif isinstance(node, KernelFunction):
-        params = [f.name for f in node.fields_accessed]
-        params += [p.name for p in node.parameters if not p.is_field_argument]
-        return "Func: %s (%s)" % (node.function_name, ",".join(params))
+        params = node.get_parameters()
+        param_names = [p.field_name for p in params if p.is_field_pointer]
+        param_names += [p.symbol.name for p in params if not p.is_field_parameter]
+        return "Func: %s (%s)" % (node.function_name, ",".join(param_names))
     elif isinstance(node, SympyAssignment):
         return repr(node.lhs)
     elif isinstance(node, Block):
diff --git a/boundaries/boundaryhandling.py b/boundaries/boundaryhandling.py
index dea8267688c115eceeb0082044c2f60524d04d7e..d60209d11beb725ba3e05c15e775c2b441d05f19 100644
--- a/boundaries/boundaryhandling.py
+++ b/boundaries/boundaryhandling.py
@@ -6,6 +6,7 @@ from pystencils.backends.cbackend import CustomCppCode
 from pystencils.boundaries.createindexlist import numpy_data_type_for_boundary_object, create_boundary_index_array
 from pystencils.cache import memorycache
 from pystencils.data_types import create_type
+from pystencils.kernelparameters import FieldPointerSymbol
 
 DEFAULT_FLAG_TYPE = np.uint32
 
@@ -204,9 +205,9 @@ class BoundaryHandling:
             for b_obj, idx_arr in b[self._index_array_name].boundary_object_to_index_list.items():
                 kwargs[self._field_name] = b[self._field_name]
                 kwargs['indexField'] = idx_arr
-                data_used_in_kernel = (p.field_name
+                data_used_in_kernel = (p.fields[0].name
                                        for p in self._boundary_object_to_boundary_info[b_obj].kernel.parameters
-                                       if p.is_field_ptr_argument and p.field_name not in kwargs)
+                                       if isinstance(p.symbol, FieldPointerSymbol) and p.fields[0].name not in kwargs)
                 kwargs.update({name: b[name] for name in data_used_in_kernel})
 
                 self._boundary_object_to_boundary_info[b_obj].kernel(**kwargs)
@@ -220,9 +221,9 @@ class BoundaryHandling:
                 arguments = kwargs.copy()
                 arguments[self._field_name] = b[self._field_name]
                 arguments['indexField'] = idx_arr
-                data_used_in_kernel = (p.field_name
+                data_used_in_kernel = (p.fields[0].name
                                        for p in self._boundary_object_to_boundary_info[b_obj].kernel.parameters
-                                       if p.is_field_ptr_argument and p.field_name not in arguments)
+                                       if isinstance(p.symbol, FieldPointerSymbol) and p.fields[0].name not in arguments)
                 arguments.update({name: b[name] for name in data_used_in_kernel if name not in arguments})
 
                 kernel = self._boundary_object_to_boundary_info[b_obj].kernel
diff --git a/cpu/cpujit.py b/cpu/cpujit.py
index 69ae6022c8e1abb35561c89abfb4d78d625a4213..cfcd4fef3fd95583c0597debd194f7d861aa20b8 100644
--- a/cpu/cpujit.py
+++ b/cpu/cpujit.py
@@ -55,10 +55,11 @@ import numpy as np
 import subprocess
 from appdirs import user_config_dir, user_cache_dir
 from collections import OrderedDict
+
+from pystencils.kernelparameters import FieldPointerSymbol, FieldStrideSymbol, FieldShapeSymbol
 from pystencils.utils import recursive_dict_update
 from sysconfig import get_paths
-from pystencils import FieldType, Field
-from pystencils.data_types import get_base_type
+from pystencils import FieldType
 from pystencils.backends.cbackend import generate_c, get_headers
 from pystencils.utils import file_handle_for_atomic_write, atomic_file_write
 
@@ -311,8 +312,8 @@ def equal_size_check(fields):
         return ""
 
     ref_field = fields[0]
-    cond = ["({field.name}_shape[{i}] == {ref_field.name}_shape[{i}])".format(ref_field=ref_field,
-                                                                              field=field_to_test, i=i)
+    cond = ["(buffer_{field.name}.shape[{i}] == buffer_{ref_field.name}.shape[{i}])".format(ref_field=ref_field,
+                                                                                            field=field_to_test, i=i)
             for field_to_test in fields[1:]
             for i in range(fields[0].spatial_dimensions)]
     cond = " && ".join(cond)
@@ -326,60 +327,45 @@ def create_function_boilerplate_code(parameter_info, name, insert_checks=True):
     variable_sized_normal_fields = set()
     variable_sized_index_fields = set()
 
-    for arg in parameter_info:
-        if arg.is_field_argument:
-            if arg.is_field_ptr_argument:
-                pre_call_code += template_extract_array.format(name=arg.field_name)
-                post_call_code += template_release_buffer.format(name=arg.field_name)
-                parameters.append("({dtype} *)buffer_{name}.buf".format(dtype=str(arg.field.dtype),
-                                                                        name=arg.field_name))
-
-                shapes = ", ".join(["buffer_{name}.shape[{i}]".format(name=arg.field_name, i=i)
-                                    for i in range(len(arg.field.strides))])
-
-                shape_type = get_base_type(Field.SHAPE_DTYPE)
-                pre_call_code += "{type} {name}_shape[] = {{ {elements} }};\n".format(type=shape_type,
-                                                                                      name=arg.field_name,
-                                                                                      elements=shapes)
-
-                item_size = get_base_type(arg.dtype).numpy_dtype.itemsize
-                strides = ["buffer_{name}.strides[{i}] / {bytes}".format(i=i, name=arg.field_name, bytes=item_size)
-                           for i in range(len(arg.field.strides))]
-                strides = ", ".join(strides)
-                strides_type = get_base_type(Field.STRIDE_DTYPE)
-                pre_call_code += "{type} {name}_strides[] = {{ {elements} }};\n".format(type=strides_type,
-                                                                                        name=arg.field_name,
-                                                                                        elements=strides)
-
-                if insert_checks and arg.field.has_fixed_shape:
-                    shape_cond = ["{name}_shape[{i}] == {s}".format(s=s, name=arg.field_name, i=i)
-                                  for i, s in enumerate(arg.field.spatial_shape)]
-                    shape_cond = " && ".join(shape_cond)
-                    pre_call_code += template_check_array.format(cond=shape_cond, what="shape", name=arg.field.name,
-                                                                 expected=str(arg.field.shape))
-
-                    strides_cond = ["({name}_strides[{i}] == {s} || {name}_shape[{i}]<=1)".format(s=s, i=i,
-                                                                                                  name=arg.field_name)
-                                    for i, s in enumerate(arg.field.spatial_strides)]
-                    strides_cond = " && ".join(strides_cond)
-                    expected_strides_str = str([e * item_size for e in arg.field.strides])
-                    pre_call_code += template_check_array.format(cond=strides_cond, what="strides", name=arg.field.name,
-                                                                 expected=expected_strides_str)
-                if insert_checks and not arg.field.has_fixed_shape:
-                    if FieldType.is_generic(arg.field):
-                        variable_sized_normal_fields.add(arg.field)
-                    elif FieldType.is_indexed(arg.field):
-                        variable_sized_index_fields.add(arg.field)
-
-            elif arg.is_field_shape_argument:
-                parameters.append("{name}_shape".format(name=arg.field_name))
-            elif arg.is_field_stride_argument:
-                parameters.append("{name}_strides".format(name=arg.field_name))
+    for param in parameter_info:
+        if param.is_field_pointer:
+            field = param.fields[0]
+            pre_call_code += template_extract_array.format(name=field.name)
+            post_call_code += template_release_buffer.format(name=field.name)
+            parameters.append("({dtype} *)buffer_{name}.buf".format(dtype=str(field.dtype), name=field.name))
+
+            if insert_checks and field.has_fixed_shape:
+                shape_cond = ["buffer_{name}.shape[{i}] == {s}".format(s=s, name=field.name, i=i)
+                              for i, s in enumerate(field.spatial_shape)]
+                shape_cond = " && ".join(shape_cond)
+                pre_call_code += template_check_array.format(cond=shape_cond, what="shape", name=field.name,
+                                                             expected=str(field.shape))
+
+                item_size = field.dtype.numpy_dtype.itemsize
+                expected_strides = [e * item_size for e in field.spatial_strides]
+                stride_check_code = "(buffer_{name}.strides[{i}] == {s} || buffer_{name}.shape[{i}]<=1)"
+                strides_cond = " && ".join([stride_check_code.format(s=s, i=i, name=field.name)
+                                            for i, s in enumerate(expected_strides)])
+                pre_call_code += template_check_array.format(cond=strides_cond, what="strides", name=field.name,
+                                                             expected=str(expected_strides))
+
+            if insert_checks and not field.has_fixed_shape:
+                if FieldType.is_generic(field):
+                    variable_sized_normal_fields.add(field)
+                elif FieldType.is_indexed(field):
+                    variable_sized_index_fields.add(field)
+        elif param.is_field_stride:
+            field = param.fields[0]
+            item_size = field.dtype.numpy_dtype.itemsize
+            parameters.append("buffer_{name}.strides[{i}] / {bytes}".format(bytes=item_size, i=param.symbol.coordinate,
+                                                                            name=field.name))
+        elif param.is_field_shape:
+            parameters.append("buffer_{name}.shape[{i}]".format(i=param.symbol.coordinate, name=param.field_name))
         else:
-            extract_function, target_type = type_mapping[arg.dtype.numpy_dtype.type]
+            extract_function, target_type = type_mapping[param.symbol.dtype.numpy_dtype.type]
             pre_call_code += template_extract_scalar.format(extract_function=extract_function, target_type=target_type,
-                                                            name=arg.name)
-            parameters.append(arg.name)
+                                                            name=param.symbol.name)
+            parameters.append(param.symbol.name)
 
     pre_call_code += equal_size_check(variable_sized_normal_fields)
     pre_call_code += equal_size_check(variable_sized_index_fields)
@@ -449,7 +435,7 @@ class ExtensionModuleCode:
             old_name = ast.function_name
             ast.function_name = "kernel_" + name
             print(generate_c(ast), file=file)
-            print(create_function_boilerplate_code(ast.parameters, name), file=file)
+            print(create_function_boilerplate_code(ast.get_parameters(), name), file=file)
             ast.function_name = old_name
         print(create_module_boilerplate_code(self.module_name, self._function_names), file=file)
 
@@ -525,4 +511,4 @@ def compile_and_load(ast):
         lib_file = compile_module(code, code_hash_str, base_dir=cache_config['object_cache'])
         result = load_kernel_from_file(code_hash_str, ast.function_name, lib_file)
 
-    return KernelWrapper(result, ast.parameters, ast)
+    return KernelWrapper(result, ast.get_parameters(), ast)
diff --git a/cpu/kernelcreation.py b/cpu/kernelcreation.py
index 87247daba4d210c490ff9ca9ec81b0d45d779daa..3b6fabaa4197ea51a4b9856aa42e87e373502fe9 100644
--- a/cpu/kernelcreation.py
+++ b/cpu/kernelcreation.py
@@ -62,7 +62,7 @@ def create_kernel(assignments: AssignmentOrAstNodeList, function_name: str = "ke
     body = ast.Block(assignments)
     loop_order = get_optimal_loop_ordering(fields_without_buffers)
     ast_node = make_loop_over_domain(body, function_name, iteration_slice=iteration_slice,
-                                                          ghost_layers=ghost_layers, loop_order=loop_order)
+                                     ghost_layers=ghost_layers, loop_order=loop_order)
     ast_node.target = 'cpu'
 
     if split_groups:
diff --git a/data_types.py b/data_types.py
index 8a9cadaba8a2e8e66e2040928913b7430a125130..14a2bc8b42ad319b65af95208273b25d02833e7d 100644
--- a/data_types.py
+++ b/data_types.py
@@ -88,8 +88,7 @@ class TypedSymbol(sp.Symbol):
         return self._dtype
 
     def _hashable_content(self):
-        super_class_contents = list(super(TypedSymbol, self)._hashable_content())
-        return tuple(super_class_contents + [hash(self._dtype)])
+        return super()._hashable_content(), hash(self._dtype)
 
     def __getnewargs__(self):
         return self.name, self.dtype
diff --git a/datahandling/parallel_datahandling.py b/datahandling/parallel_datahandling.py
index 6bdce83a61bf5476936ee504627d3d075777f44b..b25cb233464a2bf61283fc19858b4f6658629512 100644
--- a/datahandling/parallel_datahandling.py
+++ b/datahandling/parallel_datahandling.py
@@ -3,6 +3,7 @@ import warnings
 from pystencils import Field
 from pystencils.datahandling.datahandling_interface import DataHandling
 from pystencils.datahandling.blockiteration import sliced_block_iteration, block_iteration
+from pystencils.kernelparameters import FieldPointerSymbol
 from pystencils.utils import DotDict
 # noinspection PyPep8Naming
 import waLBerla as wlb
@@ -228,9 +229,9 @@ class ParallelDataHandling(DataHandling):
         else:
             name_map = self._field_name_to_cpu_data_name
             to_array = wlb.field.toArray
-        data_used_in_kernel = [(name_map[p.field_name], self.fields[p.field_name])
+        data_used_in_kernel = [(name_map[p.symbol.field_name], self.fields[p.symbol.field_name])
                                for p in kernel_function.parameters if
-                               p.is_field_ptr_argument and p.field_name not in kwargs]
+                               isinstance(p.symbol, FieldPointerSymbol) and p.symbol.field_name not in kwargs]
 
         result = []
         for block in self.blocks:
diff --git a/field.py b/field.py
index 7422f1dbe52d4a1c44542862cfe7c944b83b2094..088c2f9fc43d7923406910ad6fcdca7f7e569338 100644
--- a/field.py
+++ b/field.py
@@ -4,9 +4,9 @@ from typing import Tuple, Sequence, Optional, List, Set
 import numpy as np
 import sympy as sp
 from sympy.core.cache import cacheit
-from sympy.tensor import IndexedBase
 from pystencils.alignedarray import aligned_empty
-from pystencils.data_types import TypedSymbol, create_type, create_composite_type_from_string, StructType
+from pystencils.data_types import create_type, create_composite_type_from_string, StructType
+from pystencils.kernelparameters import FieldShapeSymbol, FieldStrideSymbol
 from pystencils.stencils import offset_to_direction_string, direction_string_to_offset
 from pystencils.sympyextensions import is_integer_sequence
 
@@ -182,15 +182,14 @@ class Field:
             index_dimensions = len(index_shape)
         if isinstance(layout, str):
             layout = spatial_layout_string_to_tuple(layout, dim=spatial_dimensions)
-        shape_symbol = IndexedBase(TypedSymbol(Field.SHAPE_PREFIX + field_name, Field.SHAPE_DTYPE), shape=(1,))
-        stride_symbol = IndexedBase(TypedSymbol(Field.STRIDE_PREFIX + field_name, Field.STRIDE_DTYPE), shape=(1,))
+
         total_dimensions = spatial_dimensions + index_dimensions
         if index_shape is None or len(index_shape) == 0:
-            shape = tuple([shape_symbol[i] for i in range(total_dimensions)])
+            shape = tuple([FieldShapeSymbol([field_name], i) for i in range(total_dimensions)])
         else:
-            shape = tuple([shape_symbol[i] for i in range(spatial_dimensions)] + list(index_shape))
+            shape = tuple([FieldShapeSymbol([field_name], i) for i in range(spatial_dimensions)] + list(index_shape))
 
-        strides = tuple([stride_symbol[i] for i in range(total_dimensions)])
+        strides = tuple([FieldStrideSymbol(field_name, i) for i in range(total_dimensions)])
 
         np_data_type = np.dtype(dtype)
         if np_data_type.fields is not None:
@@ -390,13 +389,6 @@ class Field:
             return False
         return self.hashable_contents() == other.hashable_contents()
 
-    PREFIX = "f"
-    STRIDE_PREFIX = PREFIX + "stride_"
-    SHAPE_PREFIX = PREFIX + "shape_"
-    STRIDE_DTYPE = create_composite_type_from_string("const int *")
-    SHAPE_DTYPE = create_composite_type_from_string("const int *")
-    DATA_PREFIX = PREFIX + "d_"
-
     # noinspection PyAttributeOutsideInit,PyUnresolvedReferences
     class Access(sp.Symbol):
         """Class representing a relative access into a `Field`.
diff --git a/gpucuda/cudajit.py b/gpucuda/cudajit.py
index 70f757a0578ea582ee38677baa2bed093125c804..f5077defb96fe626678c0b674ed9e1281db12149 100644
--- a/gpucuda/cudajit.py
+++ b/gpucuda/cudajit.py
@@ -1,6 +1,7 @@
 import numpy as np
 from pystencils.backends.cbackend import generate_c
-from pystencils.transformations import symbol_name_to_variable_name
+from pystencils.kernelparameters import FieldPointerSymbol, FieldStrideSymbol, FieldShapeSymbol
+from pystencils.sympyextensions import symbol_name_to_variable_name
 from pystencils.data_types import StructType, get_base_type
 from pystencils.field import FieldType
 
@@ -33,7 +34,7 @@ def make_python_function(kernel_function_node, argument_dict=None):
     mod = SourceModule(code, options=["-w", "-std=c++11", "-Wno-deprecated-gpu-targets"])
     func = mod.get_function(kernel_function_node.function_name)
 
-    parameters = kernel_function_node.parameters
+    parameters = kernel_function_node.get_parameters()
 
     cache = {}
     cache_values = []
@@ -60,40 +61,37 @@ def make_python_function(kernel_function_node, argument_dict=None):
             func(*args, **block_and_thread_numbers)
         # cuda.Context.synchronize() # useful for debugging, to get errors right after kernel was called
     wrapper.ast = kernel_function_node
-    wrapper.parameters = kernel_function_node.parameters
+    wrapper.parameters = kernel_function_node.get_parameters()
     wrapper.num_regs = func.num_regs
     return wrapper
 
 
 def _build_numpy_argument_list(parameters, argument_dict):
-    import pycuda.driver as cuda
-
     argument_dict = {symbol_name_to_variable_name(k): v for k, v in argument_dict.items()}
     result = []
-    for arg in parameters:
-        if arg.is_field_argument:
-            field = argument_dict[arg.field_name]
-            if arg.is_field_ptr_argument:
-                actual_type = field.dtype
-                expected_type = arg.dtype.base_type.numpy_dtype
-                if expected_type != actual_type:
-                    raise ValueError("Data type mismatch for field '%s'. Expected '%s' got '%s'." %
-                                     (arg.field_name, expected_type, actual_type))
-                result.append(field)
-            elif arg.is_field_stride_argument:
-                dtype = get_base_type(arg.dtype).numpy_dtype
-                stride_arr = np.array(field.strides, dtype=dtype) // field.dtype.itemsize
-                result.append(cuda.In(stride_arr))
-            elif arg.is_field_shape_argument:
-                dtype = get_base_type(arg.dtype).numpy_dtype
-                shape_arr = np.array(field.shape, dtype=dtype)
-                result.append(cuda.In(shape_arr))
-            else:
-                assert False
+
+    for param in parameters:
+        if param.is_field_pointer:
+            array = argument_dict[param.field_name]
+            actual_type = array.dtype
+            expected_type = param.fields[0].dtype.numpy_dtype
+            if expected_type != actual_type:
+                raise ValueError("Data type mismatch for field '%s'. Expected '%s' got '%s'." %
+                                 (param.field_name, expected_type, actual_type))
+            result.append(array)
+        elif param.is_field_stride:
+            cast_to_dtype = param.symbol.dtype.numpy_dtype.type
+            array = argument_dict[param.field_name]
+            stride = cast_to_dtype(array.strides[param.symbol.coordinate] // array.dtype.itemsize)
+            result.append(stride)
+        elif param.is_field_shape:
+            cast_to_dtype = param.symbol.dtype.numpy_dtype.type
+            array = argument_dict[param.field_name]
+            result.append(cast_to_dtype(array.shape[param.symbol.coordinate]))
         else:
-            param = argument_dict[arg.name]
-            expected_type = arg.dtype.numpy_dtype
-            result.append(expected_type.type(param))
+            expected_type = param.symbol.dtype.numpy_dtype
+            result.append(expected_type.type(argument_dict[param.symbol.name]))
+
     assert len(result) == len(parameters)
     return result
 
@@ -106,34 +104,35 @@ def _check_arguments(parameter_specification, argument_dict):
     argument_dict = {symbol_name_to_variable_name(k): v for k, v in argument_dict.items()}
     array_shapes = set()
     index_arr_shapes = set()
-    for arg in parameter_specification:
-        if arg.is_field_argument:
+
+    for param in parameter_specification:
+        if isinstance(param.symbol, FieldPointerSymbol):
+            symbolic_field = param.fields[0]
+
             try:
-                field_arr = argument_dict[arg.field_name]
+                field_arr = argument_dict[symbolic_field.name]
             except KeyError:
-                raise KeyError("Missing field parameter for kernel call " + arg.field_name)
-
-            symbolic_field = arg.field
-            if arg.is_field_ptr_argument:
-                if symbolic_field.has_fixed_shape:
-                    symbolic_field_shape = tuple(int(i) for i in symbolic_field.shape)
-                    if isinstance(symbolic_field.dtype, StructType):
-                        symbolic_field_shape = symbolic_field_shape[:-1]
-                    if symbolic_field_shape != field_arr.shape:
-                        raise ValueError("Passed array '%s' has shape %s which does not match expected shape %s" %
-                                         (arg.field_name, str(field_arr.shape), str(symbolic_field.shape)))
-                if symbolic_field.has_fixed_shape:
-                    symbolic_field_strides = tuple(int(i) * field_arr.dtype.itemsize for i in symbolic_field.strides)
-                    if isinstance(symbolic_field.dtype, StructType):
-                        symbolic_field_strides = symbolic_field_strides[:-1]
-                    if symbolic_field_strides != field_arr.strides:
-                        raise ValueError("Passed array '%s' has strides %s which does not match expected strides %s" %
-                                         (arg.field_name, str(field_arr.strides), str(symbolic_field_strides)))
-
-                if FieldType.is_indexed(symbolic_field):
-                    index_arr_shapes.add(field_arr.shape[:symbolic_field.spatial_dimensions])
-                elif FieldType.is_generic(symbolic_field):
-                    array_shapes.add(field_arr.shape[:symbolic_field.spatial_dimensions])
+                raise KeyError("Missing field parameter for kernel call " + str(symbolic_field))
+
+            if symbolic_field.has_fixed_shape:
+                symbolic_field_shape = tuple(int(i) for i in symbolic_field.shape)
+                if isinstance(symbolic_field.dtype, StructType):
+                    symbolic_field_shape = symbolic_field_shape[:-1]
+                if symbolic_field_shape != field_arr.shape:
+                    raise ValueError("Passed array '%s' has shape %s which does not match expected shape %s" %
+                                     (symbolic_field.name, str(field_arr.shape), str(symbolic_field.shape)))
+            if symbolic_field.has_fixed_shape:
+                symbolic_field_strides = tuple(int(i) * field_arr.dtype.itemsize for i in symbolic_field.strides)
+                if isinstance(symbolic_field.dtype, StructType):
+                    symbolic_field_strides = symbolic_field_strides[:-1]
+                if symbolic_field_strides != field_arr.strides:
+                    raise ValueError("Passed array '%s' has strides %s which does not match expected strides %s" %
+                                     (symbolic_field.name, str(field_arr.strides), str(symbolic_field_strides)))
+
+            if FieldType.is_indexed(symbolic_field):
+                index_arr_shapes.add(field_arr.shape[:symbolic_field.spatial_dimensions])
+            elif FieldType.is_generic(symbolic_field):
+                array_shapes.add(field_arr.shape[:symbolic_field.spatial_dimensions])
 
     if len(array_shapes) > 1:
         raise ValueError("All passed arrays have to have the same size " + str(array_shapes))
diff --git a/kerncraft_coupling/generate_benchmark.py b/kerncraft_coupling/generate_benchmark.py
index 61b544563d8883c728cd10eb9ef7dd66e1c3cd31..ed0e65999efd15a08e4f95de46913640033f1644 100644
--- a/kerncraft_coupling/generate_benchmark.py
+++ b/kerncraft_coupling/generate_benchmark.py
@@ -87,14 +87,14 @@ def generate_benchmark(ast, likwid=False):
     constants = []
     fields = []
     call_parameters = []
-    for p in ast.parameters:
-        if not p.is_field_argument:
-            constants.append((p.name, str(p.dtype)))
-            call_parameters.append(p.name)
+    for p in ast.get_parameters():
+        if not p.is_field_parameter:
+            constants.append((p.symbol.name, str(p.symbol.dtype)))
+            call_parameters.append(p.symbol.name)
         else:
-            assert p.is_field_ptr_argument, "Benchmark implemented only for kernels with fixed loop size"
+            assert p.is_field_pointer, "Benchmark implemented only for kernels with fixed loop size"
             field = accessed_fields[p.field_name]
-            dtype = str(get_base_type(p.dtype))
+            dtype = str(get_base_type(p.symbol.dtype))
             fields.append((p.field_name, dtype, prod(field.shape)))
             call_parameters.append(p.field_name)
 
diff --git a/kerncraft_coupling/kerncraft_interface.py b/kerncraft_coupling/kerncraft_interface.py
index 79d494bfdd865b80ed0e4ab9e649e3c645c30f35..725e842e746cfc876da2f1c476a299de26a6c282 100644
--- a/kerncraft_coupling/kerncraft_interface.py
+++ b/kerncraft_coupling/kerncraft_interface.py
@@ -67,10 +67,10 @@ class PyStencilsKerncraftKernel(kerncraft.kernel.Kernel):
             permuted_shape = list(field.shape[i] for i in layout)
             self.set_variable(field.name, str(field.dtype), tuple(permuted_shape))
 
-        for param in ast.parameters:
-            if not param.is_field_argument:
-                self.set_variable(param.name, str(param.dtype), None)
-                self.sources[param.name] = [None]
+        for param in ast.get_parameters():
+            if not param.is_field_parameter:
+                self.set_variable(param.symbol.name, str(param.symbol.dtype), None)
+                self.sources[param.symbol.name] = [None]
 
         # data type
         self.datatype = list(self.variables.values())[0][0]
diff --git a/kernelparameters.py b/kernelparameters.py
new file mode 100644
index 0000000000000000000000000000000000000000..55e5ccbdb9e3bc64b46edd8db7f74df9f392195a
--- /dev/null
+++ b/kernelparameters.py
@@ -0,0 +1,92 @@
+"""Special symbols representing kernel parameters related to fields/arrays.
+
+A `KernelFunction` node determines parameters that have to be passed to the function by searching for all undefined
+symbols. Some symbols are not directly defined by the user, but are related to the `Field`s used in the kernel:
+For each field a `FieldPointerSymbol` needs to be passed in, which is the pointer to the memory region where
+the field is stored. This pointer is represented by the `FieldPointerSymbol` class that additionally stores the
+name of the corresponding field. For fields where the size is not known at compile time, additionally shape and stride
+information has to be passed in at runtime. These values are represented by  `FieldShapeSymbol`
+and `FieldPointerSymbol`.
+
+The special symbols in this module store only the field name instead of a field reference. Storing a field reference
+directly leads to problems with copying and pickling behaviour due to the circular dependency of `Field` and
+e.g. `FieldShapeSymbol`, since a Field contains `FieldShapeSymbol`s in its shape, and a `FieldShapeSymbol`
+would reference back to the field.
+"""
+from sympy.core.cache import cacheit
+from pystencils.data_types import TypedSymbol, create_composite_type_from_string, PointerType, get_base_type
+from pystencils.sympyextensions import symbol_name_to_variable_name
+
+SHAPE_DTYPE = create_composite_type_from_string("const int64")
+STRIDE_DTYPE = create_composite_type_from_string("const int64")
+
+
+class FieldStrideSymbol(TypedSymbol):
+    """Sympy symbol representing the stride value of a field in a specific coordinate."""
+    def __new__(cls, *args, **kwds):
+        obj = FieldStrideSymbol.__xnew_cached_(cls, *args, **kwds)
+        return obj
+
+    def __new_stage2__(cls, field_name, coordinate):
+        name = "_stride_{name}_{i}".format(name=symbol_name_to_variable_name(field_name), i=coordinate)
+        obj = super(FieldStrideSymbol, cls).__xnew__(cls, name, STRIDE_DTYPE)
+        obj.field_name = field_name
+        obj.coordinate = coordinate
+        return obj
+
+    def __getnewargs__(self):
+        return self.field_name, self.coordinate
+
+    __xnew__ = staticmethod(__new_stage2__)
+    __xnew_cached_ = staticmethod(cacheit(__new_stage2__))
+
+    def _hashable_content(self):
+        return super()._hashable_content(), self.coordinate, self.field_name
+
+
+class FieldShapeSymbol(TypedSymbol):
+    """Sympy symbol representing the shape value of a sequence of fields. In a kernel iterating over multiple fields
+    there is only one set of `FieldShapeSymbol`s since all the fields have to be of equal size."""
+    def __new__(cls, *args, **kwds):
+        obj = FieldShapeSymbol.__xnew_cached_(cls, *args, **kwds)
+        return obj
+
+    def __new_stage2__(cls, field_names, coordinate):
+        names = "_".join([symbol_name_to_variable_name(field_name) for field_name in field_names])
+        name = "_size_{names}_{i}".format(names=names, i=coordinate)
+        obj = super(FieldShapeSymbol, cls).__xnew__(cls, name, SHAPE_DTYPE)
+        obj.field_names = tuple(field_names)
+        obj.coordinate = coordinate
+        return obj
+
+    def __getnewargs__(self):
+        return self.field_names, self.coordinate
+
+    __xnew__ = staticmethod(__new_stage2__)
+    __xnew_cached_ = staticmethod(cacheit(__new_stage2__))
+
+    def _hashable_content(self):
+        return super()._hashable_content(), self.coordinate, self.field_names
+
+
+class FieldPointerSymbol(TypedSymbol):
+    """Sympy symbol representing the pointer to the beginning of the field data."""
+    def __new__(cls, *args, **kwds):
+        obj = FieldPointerSymbol.__xnew_cached_(cls, *args, **kwds)
+        return obj
+
+    def __new_stage2__(cls, field_name, field_dtype, const):
+        name = "_data_{name}".format(name=symbol_name_to_variable_name(field_name))
+        dtype = PointerType(get_base_type(field_dtype), const=const, restrict=False)
+        obj = super(FieldPointerSymbol, cls).__xnew__(cls, name, dtype)
+        obj.field_name = field_name
+        return obj
+
+    def __getnewargs__(self):
+        return self.field_name, self.dtype, self.dtype.const
+
+    def _hashable_content(self):
+        return super()._hashable_content(), self.field_name
+
+    __xnew__ = staticmethod(__new_stage2__)
+    __xnew_cached_ = staticmethod(cacheit(__new_stage2__))
diff --git a/llvm/llvm.py b/llvm/llvm.py
index 1b165a2f910757b9ce1b666efe26527b97d09041..c71fb5ab666fcb15b94bfe30af2611edbdfe3322 100644
--- a/llvm/llvm.py
+++ b/llvm/llvm.py
@@ -158,8 +158,9 @@ class LLVMPrinter(Printer):
         # KernelFunction does not posses a return type
         return_type = self.void
         parameter_type = []
-        for parameter in func.parameters:
-            parameter_type.append(to_llvm_type(parameter.dtype))
+        parameters = func.get_parameters()
+        for parameter in parameters:
+            parameter_type.append(to_llvm_type(parameter.symbol.dtype))
         func_type = ir.FunctionType(return_type, tuple(parameter_type))
         name = func.function_name
         fn = ir.Function(self.module, func_type, name)
@@ -167,8 +168,8 @@ class LLVMPrinter(Printer):
 
         # set proper names to arguments
         for i, arg in enumerate(fn.args):
-            arg.name = func.parameters[i].name
-            self.func_arg_map[func.parameters[i].name] = arg
+            arg.name = parameters[i].symbol.name
+            self.func_arg_map[parameters[i].symbol.name] = arg
 
         # func.attributes.add("inlinehint")
         # func.attributes.add("argmemonly")
@@ -201,6 +202,9 @@ class LLVMPrinter(Printer):
         self.func_arg_map[assignment.lhs.name] = expr
         return expr
 
+    def _print_boolean_cast_func(self, conversion):
+        return self._print_cast_func(conversion)
+
     def _print_cast_func(self, conversion):
         node = self._print(conversion.args[0])
         to_dtype = get_type_of_expression(conversion)
diff --git a/llvm/llvmjit.py b/llvm/llvmjit.py
index efc0d9bc518dd9eb3b66aacb8fa65bb3e0584654..9312693d9a2ea2a288732f5b6a82285b7b8c1499 100644
--- a/llvm/llvmjit.py
+++ b/llvm/llvmjit.py
@@ -5,7 +5,7 @@ import ctypes as ct
 from pystencils.data_types import create_composite_type_from_string
 from ..data_types import to_ctypes, ctypes_from_llvm, StructType, get_base_type
 from .llvm import generate_llvm
-from pystencils.transformations import symbol_name_to_variable_name
+from pystencils.sympyextensions import symbol_name_to_variable_name
 from pystencils.field import FieldType
 
 
@@ -15,55 +15,53 @@ def build_ctypes_argument_list(parameter_specification, argument_dict):
     array_shapes = set()
     index_arr_shapes = set()
 
-    for arg in parameter_specification:
-        if arg.is_field_argument:
+    for param in parameter_specification:
+        if param.is_field_parameter:
             try:
-                field_arr = argument_dict[arg.field_name]
+                field_arr = argument_dict[param.field_name]
             except KeyError:
-                raise KeyError("Missing field parameter for kernel call " + arg.field_name)
+                raise KeyError("Missing field parameter for kernel call " + param.field_name)
 
-            symbolic_field = arg.field
-            if arg.is_field_ptr_argument:
-                ct_arguments.append(field_arr.ctypes.data_as(to_ctypes(arg.dtype)))
+            symbolic_field = param.fields[0]
+            if param.is_field_pointer:
+                ct_arguments.append(field_arr.ctypes.data_as(to_ctypes(param.symbol.dtype)))
                 if symbolic_field.has_fixed_shape:
                     symbolic_field_shape = tuple(int(i) for i in symbolic_field.shape)
                     if isinstance(symbolic_field.dtype, StructType):
                         symbolic_field_shape = symbolic_field_shape[:-1]
                     if symbolic_field_shape != field_arr.shape:
                         raise ValueError("Passed array '%s' has shape %s which does not match expected shape %s" %
-                                         (arg.field_name, str(field_arr.shape), str(symbolic_field.shape)))
+                                         (param.field_name, str(field_arr.shape), str(symbolic_field.shape)))
                 if symbolic_field.has_fixed_shape:
                     symbolic_field_strides = tuple(int(i) * field_arr.itemsize for i in symbolic_field.strides)
                     if isinstance(symbolic_field.dtype, StructType):
                         symbolic_field_strides = symbolic_field_strides[:-1]
                     if symbolic_field_strides != field_arr.strides:
                         raise ValueError("Passed array '%s' has strides %s which does not match expected strides %s" %
-                                         (arg.field_name, str(field_arr.strides), str(symbolic_field_strides)))
+                                         (param.field_name, str(field_arr.strides), str(symbolic_field_strides)))
 
                 if FieldType.is_indexed(symbolic_field):
                     index_arr_shapes.add(field_arr.shape[:symbolic_field.spatial_dimensions])
                 elif FieldType.is_generic(symbolic_field):
                     array_shapes.add(field_arr.shape[:symbolic_field.spatial_dimensions])
 
-            elif arg.is_field_shape_argument:
-                data_type = to_ctypes(get_base_type(arg.dtype))
-                ct_arguments.append(field_arr.ctypes.shape_as(data_type))
-            elif arg.is_field_stride_argument:
-                data_type = to_ctypes(get_base_type(arg.dtype))
-                strides = field_arr.ctypes.strides_as(data_type)
-                for i in range(len(field_arr.shape)):
-                    assert strides[i] % field_arr.itemsize == 0
-                    strides[i] //= field_arr.itemsize
-                ct_arguments.append(strides)
+            elif param.is_field_shape:
+                data_type = to_ctypes(param.symbol.dtype)
+                ct_arguments.append(data_type(field_arr.shape[param.symbol.coordinate]))
+            elif param.is_field_stride:
+                data_type = to_ctypes(param.symbol.dtype)
+                assert field_arr.strides[param.symbol.coordinate] % field_arr.itemsize == 0
+                item_stride = field_arr.strides[param.symbol.coordinate] // field_arr.itemsize
+                ct_arguments.append(data_type(item_stride))
             else:
                 assert False
         else:
             try:
-                param = argument_dict[arg.name]
+                value = argument_dict[param.symbol.name]
             except KeyError:
-                raise KeyError("Missing parameter for kernel call " + arg.name)
-            expected_type = to_ctypes(arg.dtype)
-            ct_arguments.append(expected_type(param))
+                raise KeyError("Missing parameter for kernel call " + param.symbol.name)
+            expected_type = to_ctypes(param.symbol.dtype)
+            ct_arguments.append(expected_type(value))
 
     if len(array_shapes) > 1:
         raise ValueError("All passed arrays have to have the same size " + str(array_shapes))
@@ -74,7 +72,7 @@ def build_ctypes_argument_list(parameter_specification, argument_dict):
 
 
 def make_python_function_incomplete_params(kernel_function_node, argument_dict, func):
-    parameters = kernel_function_node.parameters
+    parameters = kernel_function_node.get_parameters()
 
     cache = {}
     cache_values = []
@@ -93,7 +91,7 @@ def make_python_function_incomplete_params(kernel_function_node, argument_dict,
             cache_values.append(kwargs)  # keep objects alive such that ids remain unique
             func(*args)
     wrapper.ast = kernel_function_node
-    wrapper.parameters = kernel_function_node.parameters
+    wrapper.parameters = kernel_function_node.get_parameters()
     return wrapper
 
 
@@ -110,7 +108,7 @@ def make_python_function(ast, argument_dict={}, func=None):
         jit = generate_and_jit(ast)
         func = jit.get_function_ptr(ast.function_name)
     try:
-        args = build_ctypes_argument_list(ast.parameters, argument_dict)
+        args = build_ctypes_argument_list(ast.get_parameters(), argument_dict)
     except KeyError:
         # not all parameters specified yet
         return make_python_function_incomplete_params(ast, argument_dict, func)
diff --git a/sympyextensions.py b/sympyextensions.py
index b95fc6bc5dc40bc1ebc3b5677367d1849fe7c10a..df236f5c8ea701b94a1a85c424b92cfe36c67725 100644
--- a/sympyextensions.py
+++ b/sympyextensions.py
@@ -549,3 +549,8 @@ def sort_assignments_topologically(assignments: Sequence[Assignment]) -> List[As
 class SymbolCreator:
     def __getattribute__(self, name):
         return sp.Symbol(name)
+
+
+def symbol_name_to_variable_name(symbol_name):
+    """Replaces characters which are allowed in sympy symbol names but not in C/C++ variable names"""
+    return symbol_name.replace("^", "_")
\ No newline at end of file
diff --git a/transformations.py b/transformations.py
index 7ef7edd2d6fe47f2c8acaa78e759969b833177fb..2f1bdf022b23b993f1fffc24237637ec465b2638 100644
--- a/transformations.py
+++ b/transformations.py
@@ -11,8 +11,10 @@ from pystencils.assignment_collection.nestedscopes import NestedScopes
 from pystencils.field import Field, FieldType
 from pystencils.data_types import TypedSymbol, PointerType, StructType, get_base_type, cast_func, \
     pointer_arithmetic_func, get_type_of_expression, collate_types, create_type
+from pystencils.kernelparameters import FieldPointerSymbol, FieldStrideSymbol
 from pystencils.slicing import normalize_slice
 import pystencils.astnodes as ast
+from pystencils.sympyextensions import symbol_name_to_variable_name
 
 
 def filtered_tree_iteration(node, node_type, stop_type=None):
@@ -154,9 +156,9 @@ def create_intermediate_base_pointer(field_access, coordinates, previous_ptr):
         >>> x, y = sp.symbols("x y")
         >>> prev_pointer = TypedSymbol("ptr", "double")
         >>> create_intermediate_base_pointer(field[1,-2](5), {0: x}, prev_pointer)
-        (ptr_01, x*fstride_myfield[0] + fstride_myfield[0])
+        (ptr_01, _stride_myfield_0*x + _stride_myfield_0)
         >>> create_intermediate_base_pointer(field[1,-2](5), {0: x, 1 : y }, prev_pointer)
-        (ptr_01_1m2, x*fstride_myfield[0] + y*fstride_myfield[1] + fstride_myfield[0] - 2*fstride_myfield[1])
+        (ptr_01_1m2, _stride_myfield_0*x + _stride_myfield_0 + _stride_myfield_1*y - 2*_stride_myfield_1)
     """
     field = field_access.field
     offset = 0
@@ -361,9 +363,7 @@ def resolve_buffer_accesses(ast_node, base_buffer_index, read_only_field_names=s
                 return expr
 
             buffer = field_access.field
-
-            dtype = PointerType(buffer.dtype, const=buffer.name in read_only_field_names, restrict=False)
-            field_ptr = TypedSymbol("%s%s" % (Field.DATA_PREFIX, symbol_name_to_variable_name(buffer.name)), dtype)
+            field_ptr = FieldPointerSymbol(buffer.name, buffer.dtype, const=buffer.name in read_only_field_names)
 
             buffer_index = base_buffer_index
             if len(field_access.index) > 1:
@@ -437,8 +437,7 @@ def resolve_field_accesses(ast_node, read_only_field_names=set(),
             else:
                 base_pointer_info = [list(range(field.index_dimensions + field.spatial_dimensions))]
 
-            dtype = PointerType(field.dtype, const=field.name in read_only_field_names, restrict=False)
-            field_ptr = TypedSymbol("%s%s" % (Field.DATA_PREFIX, symbol_name_to_variable_name(field.name)), dtype)
+            field_ptr = FieldPointerSymbol(field.name, field.dtype, const=field.name in read_only_field_names)
 
             def create_coordinate_dict(group_param):
                 coordinates = {}
@@ -718,11 +717,6 @@ def cleanup_blocks(node: ast.Node) -> None:
             cleanup_blocks(a)
 
 
-def symbol_name_to_variable_name(symbol_name):
-    """Replaces characters which are allowed in sympy symbol names but not in C/C++ variable names"""
-    return symbol_name.replace("^", "_")
-
-
 class KernelConstraintsCheck:
     """Checks if the input to create_kernel is valid.
 
@@ -1078,7 +1072,7 @@ def replace_inner_stride_with_one(ast_node: ast.KernelFunction) -> None:
 
     inner_loop_counter = inner_loop_counters.pop()
 
-    stride_params = [p for p in ast_node.parameters if p.is_field_stride_argument]
+    stride_params = [p for p in ast_node.get_parameters() if isinstance(p.symbol, FieldStrideSymbol)]
     subs_dict = {}
     for stride_param in stride_params:
         stride_symbol = stride_param.symbol