Skip to content
Snippets Groups Projects
Commit fed74103 authored by René Heß's avatar René Heß
Browse files

Remove option attributes from option arrays

As a side effect we removed the dune-testtools dependency.
parent 9bcd6c16
No related branches found
No related tags found
No related merge requests found
......@@ -6,8 +6,8 @@ from pytools import ImmutableRecord, memoize
import cerberus
import yaml
import pkg_resources
from dune.testtools.parametertree.parser import parse_ini_file
from six.moves import configparser
from six import StringIO
class CodegenOptionsValidator(cerberus.Validator):
......@@ -21,140 +21,39 @@ class CodegenOptionsValidator(cerberus.Validator):
return True
class CodegenOption(ImmutableRecord):
""" Data structure representing a single formcompiler option """
def __init__(self,
default=None,
helpstr="Undocumented feature!",
process=lambda x: x,
_type=type(None),
):
_type = type(default)
if issubclass(_type, type(None)):
_type = str
ImmutableRecord.__init__(self,
helpstr=helpstr,
default=default,
type=_type,
)
def _load_scheme(form=False):
resource_package = __name__
if form:
resource_path = 'options_form.yaml'
else:
resource_path = 'options_global.yaml'
yaml_stream = pkg_resources.resource_string(resource_package, resource_path)
try:
scheme = yaml.safe_load(yaml_stream)
except Exception as e:
raise e
return scheme
class CodegenGlobalOptionsArray(ImmutableRecord):
""" A collection of form compiler arguments """
def __init__(self, **kwargs):
# Set the default values from the yaml scheme as defaults
resource_package = __name__
resource_path = 'options_global.yaml'
yaml_stream = pkg_resources.resource_string(resource_package, resource_path)
try:
scheme = yaml.safe_load(yaml_stream)
except Exception as e:
raise e
scheme = _load_scheme()
opts = {k: v['default'] for k, v in scheme.items()}
opts.update(**kwargs)
ImmutableRecord.__init__(self, **opts)
# Arguments that are to be set from the outside
uflfile = CodegenOption(helpstr="the UFL file to compile")
debug_cache_with_stack = CodegenOption(default=False, helpstr="Store stack along with cache objects. Makes debugging caching issues easier.")
driver_file = CodegenOption(helpstr="The filename for the generated driver header")
explicit_time_stepping = CodegenOption(default=False, helpstr="use explicit time stepping")
exact_solution_expression = CodegenOption(helpstr="name of the exact solution expression in the ufl file")
compare_l2errorsquared = CodegenOption(helpstr="maximal allowed l2 error squared of difference between numerical solution and interpolation of exact solution (NOTE: requires --exact-solution-expression)")
grid_info = CodegenOption(default=None, helpstr="Path to file with information about facedir and facemod variations. This can be used to limit the generation of skeleton kernels.")
l2error_tree_path = CodegenOption(default=None, helpstr="Tree pathes that should be considered for l2 error calculation. Default None means we take all of them into account.")
ini_file = CodegenOption(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)")
opcounter = CodegenOption(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.")
performance_measuring = CodegenOption(default=False, helpstr="Generate opcounter codepath, but only measure times!")
instrumentation_level = CodegenOption(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)")
project_basedir = CodegenOption(helpstr="The base (build) directory of the dune-codegen project")
architecture = CodegenOption(default="haswell", helpstr="The architecture to optimize for. Possible values: haswell|knl|skylake")
yaspgrid_offset = CodegenOption(default=False, helpstr="Set to true if you want a yasp grid where the lower left corner is not in the origin.")
grid_unstructured = CodegenOption(default=False, helpstr="Set to true if you want to use an unstructured grid.")
precision_bits = CodegenOption(default=64, helpstr="The number of bits for the floating point type")
overlapping = CodegenOption(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.")
operators = CodegenOption(default="r", helpstr="A comma separated list of operators, each name will be interpreted as a subsection name within the formcompiler section")
target_name = CodegenOption(default=None, helpstr="The target name from CMake")
operator_to_build = CodegenOption(default=None, helpstr="The operators from the list that is about to be build now. CMake sets this one!!!")
debug_interpolate_input = CodegenOption(default=False, helpstr="Should the input for printresidual and printmatix be interpolated (instead of random input).")
use_likwid = CodegenOption(default=False, helpstr="Use likwid instead of own performance measurements.")
autotune_google_benchmark = CodegenOption(default=False, helpstr="Use google-benchmark library for autotuning (when autotuning is activated).")
# Arguments that are mainly to be set by logic depending on other options
max_vector_width = CodegenOption(default=256, helpstr=None)
parallel = CodegenOption(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.")
class CodegenFormOptionsArray(ImmutableRecord):
""" A collection of form-specific form compiler arguments """
def __init__(self, **kwargs):
# opts = {k: v.default for k, v in CodegenFormOptionsArray.__dict__.items() if isinstance(v, CodegenOption)}
# Set the default values from the yaml scheme as defaults
resource_package = __name__
resource_path = 'options_form.yaml'
yaml_stream = pkg_resources.resource_string(resource_package, resource_path)
try:
scheme = yaml.safe_load(yaml_stream)
except Exception as e:
raise e
scheme = _load_scheme(form=True)
opts = {k: v['default'] for k, v in scheme.items()}
opts.update(**kwargs)
ImmutableRecord.__init__(self, **opts)
# Form specific options
form = CodegenOption(default=None, helpstr="The name of the UFL object representing the form in the UFL file")
filename = CodegenOption(default=None, helpstr="The filename to use for this LocalOperator")
classname = CodegenOption(default=None, helpstr="The name of the C++ class to generate")
numerical_jacobian = CodegenOption(default=False, helpstr="use numerical jacobians (only makes sense, if uflpdelab for some reason fails to generate analytic jacobians)")
matrix_free = CodegenOption(default=False, helpstr="Generate jacobian_apply_* methods for matrix free solvers")
print_transformations = CodegenOption(default=False, helpstr="print out dot files after ufl tree transformations")
print_transformations_dir = CodegenOption(default=".", helpstr="place where to put dot files (can be omitted)")
quadrature_order = CodegenOption(_type=int, helpstr="Quadrature order used for all integrals.")
diagonal_transformation_matrix = CodegenOption(default=False, helpstr="set option if the jacobian of the transformation is diagonal (axiparallel grids)")
constant_transformation_matrix = CodegenOption(default=False, helpstr="set option if the jacobian of the transformation is constant on a cell")
fastdg = CodegenOption(default=False, helpstr="Use FastDGGridOperator from PDELab.")
sumfact = CodegenOption(default=False, helpstr="Use sumfactorization")
sumfact_regular_jacobians = CodegenOption(default=False, helpstr="Generate non sum-factorized jacobians (only useful if sumfact is set)")
sumfact_on_boundary = CodegenOption(default=True, helpstr="Whether boundary integrals should be vectorized. It might not be worth the hassle...")
sumfact_optimize_loop_order = CodegenOption(default=False, helpstr="Optimize order of loops in sumf factorization function using autotuning.")
sumfact_performance_transformations = CodegenOption(default=False, helpstr="Apply sum factorization specific performance transformations.")
sumfact_performance_transformations_testrun = CodegenOption(default=0, helpstr="If larger than zero determines test case to run.")
vectorization_quadloop = CodegenOption(default=False, helpstr="whether to generate code with explicit vectorization")
vectorization_strategy = CodegenOption(default="none", helpstr="The identifier of the vectorization cost model. Possible values: none|explicit|model|target|autotune")
vectorization_not_fully_vectorized_error = CodegenOption(default=False, helpstr="throw an error if nonquadloop vectorization did not fully vectorize")
vectorization_horizontal = CodegenOption(default=None, helpstr="an explicit value for horizontal vectorization read by the 'explicit' strategy")
vectorization_vertical = CodegenOption(default=None, helpstr="an explicit value for vertical vectorization read by the 'explicit' strategy")
vectorization_padding = CodegenOption(default=None, helpstr="an explicit value for the allowed padding in vectorization")
vectorization_allow_quadrature_changes = CodegenOption(default=False, helpstr="whether the vectorization strategy is allowed to alter quadrature point numbers")
vectorization_list_index = CodegenOption(default=None, helpstr="Which vectorization to pick from a list (only valid with vectorization_strategy=fromlist).")
vectorization_jacobians = CodegenOption(default=True, helpstr="Whether to attempt to vectorize jacobians (takes time, often not needed)")
vectorization_target = CodegenOption(_type=float, helpstr="The cost function target for the 'target' cost model. Only needed to verify the cost model itself, do not use light-heartedly!!!")
simplify = CodegenOption(default=False, helpstr="Whether to simplify expressions using sympy")
generate_jacobians = CodegenOption(default=True, helpstr="Whether jacobian_* methods should be generated. This is set to false automatically, when numerical_jacobian is set to true.")
generate_jacobian_apply = CodegenOption(default=False, helpstr="Wether jacobian_allpy_* methods should be generated.")
generate_residuals = CodegenOption(default=True, helpstr="Whether alpha_* methods should be generated.")
unroll_dimension_loops = CodegenOption(default=False, helpstr="whether loops over the geometric dimension should be unrolled")
blockstructured = CodegenOption(default=False, helpstr="Use block structure")
number_of_blocks = CodegenOption(default=1, helpstr="Number of sub blocks in one direction")
vectorization_blockstructured = CodegenOption(default=False, helpstr="Vectorize block structuring")
vectorization_blockstructured_tail = CodegenOption(default=True, helpstr="Try to fully vectorize block structuring even when 'nunmber_of_blocks' is not divisible by vector length")
vectorization_blockstructured_tail_ordering = CodegenOption(default='consecutive', helpstr="Ordering of the tail w.r.t the vectorized loop. Possible values: consecutive|blocked")
adjoint = CodegenOption(default=False, helpstr="Generate adjoint operator")
control = CodegenOption(default=False, helpstr="Generate operator of derivative w.r.t. the control variable")
objective_function = CodegenOption(default=None, helpstr="Name of form representing the objective function in UFL file")
control_variable = CodegenOption(default=None, helpstr="Name of control variable in UFL file")
block_preconditioner_diagonal = CodegenOption(default=False, helpstr="Whether this operator should implement the diagonal part of a block preconditioner")
block_preconditioner_offdiagonal = CodegenOption(default=False, helpstr="Whether this operator should implement the off-diagonal part of a block preconditioner")
geometry_mixins = CodegenOption(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")
quadrature_mixins = CodegenOption(default="generic", helpstr="A comma separated list of mixin identifiers to use for quadrature. Currently implemented: generic, sumfact")
basis_mixins = CodegenOption(default="generic", helpstr="A comma separated list of mixin identifiers to use for basis function evaluation. Currently implemented: generic, sumfact")
accumulation_mixins = CodegenOption(default="generic", helpstr="A comma separated list of mixin identifiers to use for accumulation. Currently implemented: generic, sumfact, control, blockstructured")
enable_volume = CodegenOption(default=True, helpstr="Whether to assemble volume integrals")
enable_skeleton = CodegenOption(default=True, helpstr="Whether to assemble skeleton integrals")
enable_boundary = CodegenOption(default=True, helpstr="Whether to assemble boundary integrals")
# Until more sophisticated logic is needed, we keep the actual option data in this module
_global_options = CodegenGlobalOptionsArray()
......@@ -168,30 +67,31 @@ def initialize_options():
_global_options = update_options_from_inifile(_global_options)
# Validate global options
resource_package = __name__
resource_path = 'options_global.yaml'
yaml_stream = pkg_resources.resource_string(resource_package, resource_path)
try:
scheme_global = yaml.safe_load(yaml_stream)
except Exception as e:
raise e
scheme_global = _load_scheme()
validator_global = CodegenOptionsValidator(scheme_global, require_all=True)
if not validator_global.validate(_global_options.__dict__):
raise RuntimeError("Global options validation failed: {}".format(validator_global.errors))
# Validate form options
resource_path = 'options_form.yaml'
yaml_stream = pkg_resources.resource_string(resource_package, resource_path)
try:
scheme_form = yaml.safe_load(yaml_stream)
except Exception as e:
raise e
scheme_form = _load_scheme(form=True)
validator_form = CodegenOptionsValidator(scheme_form, require_all=True)
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 _scheme_type_to_type(scheme_type):
assert isinstance(scheme_type, str)
if scheme_type == 'string':
return str
if scheme_type == 'boolean':
return bool
if scheme_type == 'integer':
return int
if scheme_type == 'float':
return float
def update_options_from_commandline(opt):
""" Return an options array object with updated values from the commandline """
assert isinstance(opt, CodegenGlobalOptionsArray)
......@@ -199,10 +99,15 @@ def update_options_from_commandline(opt):
epilog="Please report bugs to dominic.kempf@iwr.uni-heidelberg.de",
)
parser.add_argument('--version', action='version', version='%(prog)s 0.1')
for k, v in type(opt).__dict__.items():
if isinstance(v, CodegenOption) and v.helpstr is not None:
# Load global options scheme
scheme = _load_scheme()
# Add all options that have a helpstr to the command line parser
for k, v in scheme.items():
if v['helpstr'] is not None:
cmdopt = "--{}".format(k.replace('_', '-'))
parser.add_argument(cmdopt, help=v.helpstr, type=v.type)
parser.add_argument(cmdopt, help=v['helpstr'], type=_scheme_type_to_type(v['type']))
parsedargs = {k: v for k, v in vars(parser.parse_args()).items() if v is not None}
return opt.copy(**parsedargs)
......@@ -210,38 +115,38 @@ def update_options_from_commandline(opt):
def update_options_from_inifile(opt):
""" Return an options array object with updated values from an inifile """
if opt.ini_file:
def parse_ini(section, opttype):
def _fix_types(k, v):
if hasattr(opttype, k) and getattr(opttype, k).type is bool:
return bool(eval(v))
if hasattr(opttype, k):
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()}
config = configparser.ConfigParser()
# Parse global options
opt = opt.copy(**parse_ini("formcompiler", CodegenGlobalOptionsArray))
# Read ini file
try:
config.read(opt.ini_file)
except configparser.MissingSectionHeaderError:
# Config parser doesn't like ini files where without section. For
# this case we introduce a [root] section on top.
ini_str = '[root]\n' + open(opt.ini_file, 'r').read()
ini_fp = StringIO(ini_str)
config = configparser.RawConfigParser()
config.readfp(ini_fp)
# Parse form-specific options
# Parse global options
scheme = _load_scheme()
options = dict(config.items('formcompiler'))
for k, v in options.items():
assert k in scheme
options[k] = _scheme_type_to_type(scheme[k]['type'])(v)
opt = opt.copy(**options)
# Parse form options
scheme = _load_scheme(form=True)
for form in [i.strip() for i in opt.operators.split(",")]:
_form_options[form] = CodegenFormOptionsArray(**parse_ini("formcompiler.{}".format(form), CodegenFormOptionsArray))
section = 'formcompiler.{}'.format(form)
options = {}
if config.has_section(section):
options = dict(config.items('formcompiler.{}'.format(form)))
for k, v in options.items():
assert k in scheme
options[k] = _scheme_type_to_type(scheme[k]['type'])(v)
_form_options[form] = CodegenFormOptionsArray(**options)
return opt
......
......@@ -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', 'cerberus', 'pyyaml'],
install_requires=['sympy', 'frozendict', 'pytest', 'pytest-pep8', 'filelock', 'cerberus', 'pyyaml'],
cmdclass={'test': PyTest},
entry_points = {
"console_scripts": [
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment