diff --git a/python/dune/codegen/options.py b/python/dune/codegen/options.py
index a4f65ce476fe2f610135234fb8d5a16b97401dec..62085e85a7ea9945e20c715c42b5d3864c79abee 100644
--- a/python/dune/codegen/options.py
+++ b/python/dune/codegen/options.py
@@ -3,10 +3,23 @@
 from argparse import ArgumentParser
 from os.path import abspath
 from pytools import ImmutableRecord, memoize
+import cerberus
+import yaml
 
 from dune.testtools.parametertree.parser import parse_ini_file
 
 
+class CodegenOptionsValidator(cerberus.Validator):
+    # A validator that accepts the helpstr field in the scheme
+    def _validate_helpstr(self, helpstr, field, value):
+        """ Describe the option
+
+        The rule's arguments are validated against this schema:
+        {'type': 'string'}
+        """
+        return True
+
+
 class CodegenOption(ImmutableRecord):
     """ Data structure representing a single formcompiler option """
     def __init__(self,
@@ -134,6 +147,30 @@ def initialize_options():
     _global_options = update_options_from_commandline(_global_options)
     _global_options = update_options_from_inifile(_global_options)
 
+    # Validate global options
+    filename = '/home/rene/phd/dune-les/dune-codegen/python/dune/codegen/options_global.yaml'
+    # filename = 'options_global.yaml'
+    with open(filename, 'r') as stream:
+        try:
+            scheme_global = yaml.safe_load(stream)
+        except Exception as e:
+            raise e
+    validator_global = CodegenOptionsValidator(scheme_global)
+    if not validator_global.validate(_global_options.__dict__):
+        raise RuntimeError("Global options validation failed: {}".format(validator_global.errors))
+
+    # Validate form options
+    filename = '/home/rene/phd/dune-les/dune-codegen/python/dune/codegen/options_form.yaml'
+    with open(filename, 'r') as stream:
+        try:
+            scheme_form = yaml.safe_load(stream)
+        except Exception as e:
+            raise e
+    validator_form = CodegenOptionsValidator(scheme_form)
+    for form in [i.strip() for i in _global_options.operators.split(",")]:
+        if not validator_form.validate(_form_options[form].__dict__):
+            raise RuntimeError("Form options validation failed: {}".format(validator_form.errors))
+
 
 def update_options_from_commandline(opt):
     """ Return an options array object with updated values from the commandline """
@@ -161,10 +198,28 @@ def update_options_from_inifile(opt):
                     return getattr(opttype, k).type(v)
                 return v
             ini = parse_ini_file(opt.ini_file).get(section, {})
+
+            # Exclude ufl.variants
+            for key in ini.keys():
+                if key.startswith('ufl_variants.'):
+                    del ini[key]
+
+            # Exclude form specific options when reading global options
+            if section == 'formcompiler':
+                operators = opt.operators.split(',')
+                if 'operators' in ini:
+                    operators = ini['operators'].split(',')
+                for form in [i.strip() for i in operators]:
+                    for key in ini.keys():
+                        if key.startswith(form + '.'):
+                            del ini[key]
+
             return {k: _fix_types(k, v) for k, v in ini.items()}
 
+        # Parse global options
         opt = opt.copy(**parse_ini("formcompiler", CodegenGlobalOptionsArray))
-        # Also parse form-specific options
+
+        # Parse form-specific options
         for form in [i.strip() for i in opt.operators.split(",")]:
             _form_options[form] = CodegenFormOptionsArray(**parse_ini("formcompiler.{}".format(form), CodegenFormOptionsArray))
 
diff --git a/python/dune/codegen/options_form.yaml b/python/dune/codegen/options_form.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..2399ab67686680df0204994b240d03cf524b927e
--- /dev/null
+++ b/python/dune/codegen/options_form.yaml
@@ -0,0 +1,200 @@
+accumulation_mixins:
+    type: string
+    default: "generic"
+    helpstr: "A comma separated list of mixin identifiers to use for accumulation. Currently implemented: generic, sumfact, control, blockstructured"
+adjoint:
+    type: boolean
+    default: False
+    helpstr: "Generate adjoint operator"
+basis_mixins:
+    type: string
+    default: "generic"
+    helpstr: "A comma separated list of mixin identifiers to use for basis function evaluation. Currently implemented: generic, sumfact"
+block_preconditioner_diagonal:
+    type: boolean
+    default: False
+    helpstr: "Whether this operator should implement the diagonal part of a block preconditioner"
+block_preconditioner_offdiagonal:
+    type: boolean
+    default: False
+    helpstr: "Whether this operator should implement the off-diagonal part of a block preconditioner"
+blockstructured:
+    type: boolean
+    default: False
+    helpstr: "Use block structure"
+classname:
+    type: string
+    default: None
+    helpstr: "The name of the C++ class to generate"
+constant_transformation_matrix:
+    type: boolean
+    default: False
+    helpstr: "set option if the jacobian of the transformation is constant on a cell"
+control:
+    type: boolean
+    default: False
+    helpstr: "Generate operator of derivative w.r.t. the control variable"
+control_variable:
+    type: string
+    default: None
+    helpstr: "Name of control variable in UFL file"
+diagonal_transformation_matrix:
+    type: boolean
+    default: False
+    helpstr: "set option if the jacobian of the transformation is diagonal (axiparallel grids)"
+enable_boundary:
+    type: boolean
+    default: True
+    helpstr: "Whether to assemble boundary integrals"
+enable_skeleton:
+    type: boolean
+    default: True
+    helpstr: "Whether to assemble skeleton integrals"
+enable_volume:
+    type: boolean
+    default: True
+    helpstr: "Whether to assemble volume integrals"
+fastdg:
+    type: boolean
+    default: False
+    helpstr: "Use FastDGGridOperator from PDELab."
+filename:
+    type: string
+    default: None
+    helpstr: "The filename to use for this LocalOperator"
+form:
+    type: string
+    default: None
+    helpstr: "The name of the UFL object representing the form in the UFL file"
+generate_jacobian_apply:
+    type: boolean
+    default: False
+    helpstr: "Wether jacobian_allpy_* methods should be generated."
+generate_jacobians:
+    type: boolean
+    default: True
+    helpstr: "Whether jacobian_* methods should be generated. This is set to false automatically, when numerical_jacobian is set to true."
+generate_residuals:
+    type: boolean
+    default: True
+    helpstr: "Whether alpha_* methods should be generated."
+geometry_mixins:
+    type: string
+    default: "generic"
+    helpstr: "A comma separated list of mixin identifiers to use for geometries. Currently implemented mixins: generic, axiparallel, equidistant, sumfact_multilinear, sumfact_axiparallel, sumfact_equidistant"
+matrix_free:
+    type: boolean
+    default: False
+    helpstr: "Generate jacobian_apply_* methods for matrix free solvers"
+number_of_blocks:
+    type: integer
+    default: 1
+    helpstr: "Number of sub blocks in one direction"
+numerical_jacobian:
+    type: boolean
+    default: False
+    helpstr: "use numerical jacobians (only makes sense, if uflpdelab for some reason fails to generate analytic jacobians)"
+objective_function:
+    type: string
+    default: None
+    helpstr: "Name of form representing the objective function in UFL file"
+print_transformations:
+    type: boolean
+    default: False
+    helpstr: "print out dot files after ufl tree transformations"
+print_transformations_dir:
+    type: string
+    default: "."
+    helpstr: "place where to put dot files (can be omitted)"
+quadrature_mixins:
+    type: string
+    default: "generic"
+    helpstr: "A comma separated list of mixin identifiers to use for quadrature. Currently implemented: generic, sumfact"
+quadrature_order:
+    type: string
+    default: ''
+    helpstr: "Quadrature order used for all integrals."
+simplify:
+    type: boolean
+    default: False
+    helpstr: "Whether to simplify expressions using sympy"
+sumfact:
+    type: boolean
+    default: False
+    helpstr: "Use sumfactorization"
+sumfact_on_boundary:
+    type: boolean
+    default: True
+    helpstr: "Whether boundary integrals should be vectorized. It might not be worth the hassle..."
+sumfact_optimize_loop_order:
+    type: boolean
+    default: False
+    helpstr: "Optimize order of loops in sumf factorization function using autotuning."
+sumfact_performance_transformations:
+    type: boolean
+    default: False
+    helpstr: "Apply sum factorization specific performance transformations."
+sumfact_performance_transformations_testrun:
+    type: integer
+    default: 0
+    helpstr: "If larger than zero determines test case to run."
+sumfact_regular_jacobians:
+    type: boolean
+    default: False
+    helpstr: "Generate non sum-factorized jacobians (only useful if sumfact is set)"
+unroll_dimension_loops:
+    type: boolean
+    default: False
+    helpstr: "whether loops over the geometric dimension should be unrolled"
+vectorization_allow_quadrature_changes:
+    type: boolean
+    default: False
+    helpstr: "whether the vectorization strategy is allowed to alter quadrature point numbers"
+vectorization_blockstructured:
+    type: boolean
+    default: False
+    helpstr: "Vectorize block structuring"
+vectorization_blockstructured_tail:
+    type: boolean
+    default: True
+    helpstr: "Try to fully vectorize block structuring even when 'nunmber_of_blocks' is not divisible by vector length"
+vectorization_blockstructured_tail_ordering:
+    type: string
+    default: 'consecutive'
+    helpstr: "Ordering of the tail w.r.t the vectorized loop. Possible values: consecutive|blocked"
+vectorization_horizontal:
+    type: string
+    default: None
+    helpstr: "an explicit value for horizontal vectorization read by the 'explicit' strategy"
+vectorization_jacobians:
+    type: boolean
+    default: True
+    helpstr: "Whether to attempt to vectorize jacobians (takes time, often not needed)"
+vectorization_list_index:
+    type: string
+    default: None
+    helpstr: "Which vectorization to pick from a list (only valid with vectorization_strategy=fromlist)."
+vectorization_not_fully_vectorized_error:
+    type: boolean
+    default: False
+    helpstr: "throw an error if nonquadloop vectorization did not fully vectorize"
+vectorization_padding:
+    type: string
+    default: None
+    helpstr: "an explicit value for the allowed padding in vectorization"
+vectorization_quadloop:
+    type: boolean
+    default: False
+    helpstr: "whether to generate code with explicit vectorization"
+vectorization_strategy:
+    type: string
+    default: "none"
+    helpstr: "The identifier of the vectorization cost model. Possible values: none|explicit|model|target|autotune"
+vectorization_target:
+    type: float
+    default: -1
+    helpstr: "The cost function target for the 'target' cost model. Only needed to verify the cost model itself, do not use light-heartedly!!!"
+vectorization_vertical:
+    type: string
+    default: None
+    helpstr: "an explicit value for vertical vectorization read by the 'explicit' strategy"
\ No newline at end of file
diff --git a/python/dune/codegen/options_global.yaml b/python/dune/codegen/options_global.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..82c3951f9c149688874bc5a6fe34ccb14790fd0a
--- /dev/null
+++ b/python/dune/codegen/options_global.yaml
@@ -0,0 +1,104 @@
+architecture:
+    type: string
+    default: "haswell"
+    helpstr: "The architecture to optimize for. Possible values: haswell|knl|skylake"
+autotune_google_benchmark:
+    type: boolean
+    default: False
+    helpstr: "Use google-benchmark library for autotuning (when autotuning is activated)."
+compare_l2errorsquared:
+    type: string
+    default: ""
+    helpstr: "maximal allowed l2 error squared of difference between numerical solution and interpolation of exact solution (NOTE: requires --exact-solution-expression)"
+debug_cache_with_stack:
+    type: boolean
+    default: False
+    helpstr: "Store stack along with cache objects. Makes debugging caching issues easier."
+debug_interpolate_input:
+    type: boolean
+    default: False
+    helpstr: "Should the input for printresidual and printmatix be interpolated (instead of random input)."
+driver_file:
+    type: string
+    default: ""
+    helpstr: "The filename for the generated driver header"
+exact_solution_expression:
+    type: string
+    default: ""
+    helpstr: "name of the exact solution expression in the ufl file"
+explicit_time_stepping:
+    type: boolean
+    default: False
+    helpstr: "use explicit time stepping"
+grid_info:
+    type: string
+    default: None
+    helpstr: "Path to file with information about facedir and facemod variations. This can be used to limit the generation of skeleton kernels."
+grid_unstructured:
+    type: boolean
+    default: False
+    helpstr: "Set to true if you want to use an unstructured grid."
+ini_file:
+    type: string
+    default: ""
+    helpstr: "An inifile to use. A generated driver will be hard-coded to it, a [formcompiler] section will be used as default values to form compiler arguments (use snake case)"
+instrumentation_level:
+    type: integer
+    default: 0
+    helpstr: "Control time/opcounter measurements. 0-do nothing, 1-measure program as a whole, 2-operator applications, 3-measure kernel (eg. alpha-volume, ...), 4-parts of kernel (eg. stage 1-3 of SF)"
+l2error_tree_path:
+    type: string
+    default: None
+    helpstr: "Tree pathes that should be considered for l2 error calculation. Default None means we take all of them into account."
+max_vector_width:
+    type: integer
+    default: 256
+    helpstr: None
+opcounter:
+    type: boolean
+    default: False
+    helpstr: "Count operations. Note: In this case only operator applications are generated since solving and operator counting does not work. You probably want to set instrumentation level>0."
+operator_to_build:
+    type: string
+    default: None
+    helpstr: "The operators from the list that is about to be build now. CMake sets this one!!!"
+operators:
+    type: string
+    default: "r"
+    helpstr: "A comma separated list of operators, each name will be interpreted as a subsection name within the formcompiler section"
+overlapping:
+    type: boolean
+    default: False
+    helpstr: "Use an overlapping solver and constraints. You still need to make sure to construct a grid with overlap! The parallel option will be set automatically."
+parallel:
+    type: boolean
+    default: False
+    helpstr: "Mark that this program should be run in parallel. If set to true the c++ code will check that there are more than 1 MPI-ranks involved and the error computation will use communication."
+performance_measuring:
+    type: boolean
+    default: False
+    helpstr: "Generate opcounter codepath, but only measure times!"
+precision_bits:
+    type: integer
+    default: 64
+    helpstr: "The number of bits for the floating point type"
+project_basedir:
+    type: string
+    default: ""
+    helpstr: "The base (build) directory of the dune-codegen project"
+target_name:
+    type: string
+    default: None
+    helpstr: "The target name from CMake"
+uflfile:
+    type: string
+    default: ""
+    helpstr: "the UFL file to compile"
+use_likwid:
+    type: boolean
+    default: False
+    helpstr: "Use likwid instead of own performance measurements."
+yaspgrid_offset:
+    type: boolean
+    default: False
+    helpstr: "Set to true if you want a yasp grid where the lower left corner is not in the origin."
\ No newline at end of file
diff --git a/python/setup.py b/python/setup.py
index 88431ad1b496afc1c41d13fae5608956274f3018..a56bf2f41c567d2c0c25331b21e4e0b4bd02be3b 100644
--- a/python/setup.py
+++ b/python/setup.py
@@ -41,7 +41,7 @@ setup(name='dune.codegen',
                 'dune.codegen.ufl',
                 'dune.codegen.ufl.transformations',
                 ],
-      install_requires=['dune.testtools', 'sympy', 'frozendict', 'pytest', 'pytest-pep8', 'filelock'],
+      install_requires=['dune.testtools', 'sympy', 'frozendict', 'pytest', 'pytest-pep8', 'filelock', 'cerberus', 'pyyaml'],
       cmdclass={'test': PyTest},
       entry_points = {
         "console_scripts": [