diff --git a/devito/arch/compiler.py b/devito/arch/compiler.py index 66b3c880d4..377237ac0c 100644 --- a/devito/arch/compiler.py +++ b/devito/arch/compiler.py @@ -185,10 +185,10 @@ def __init__(self): _cstd = 'c99' def __init__(self, **kwargs): - _name = kwargs.pop('name', self.__class__.__name__) - if isinstance(_name, Compiler): - _name = _name.name - self._name = _name + name = kwargs.pop('name', self.__class__.__name__) + if isinstance(name, Compiler): + name = name.name + self._name = name super().__init__(**kwargs) diff --git a/devito/data/allocators.py b/devito/data/allocators.py index 1e00be3f20..4ccd7cddfc 100644 --- a/devito/data/allocators.py +++ b/devito/data/allocators.py @@ -1,6 +1,4 @@ import abc -from functools import reduce -from operator import mul import ctypes from ctypes.util import find_library import mmap @@ -11,7 +9,7 @@ from devito.logger import logger from devito.parameters import configuration -from devito.tools import is_integer, dtype_alloc_ctype +from devito.tools import is_integer, infer_datasize __all__ = ['ALLOC_ALIGNED', 'ALLOC_NUMA_LOCAL', 'ALLOC_NUMA_ANY', 'ALLOC_KNL_MCDRAM', 'ALLOC_KNL_DRAM', 'ALLOC_GUARD', @@ -92,8 +90,7 @@ def initialize(cls): return def alloc(self, shape, dtype, padding=0): - ctype, c_scale = dtype_alloc_ctype(dtype) - datasize = int(reduce(mul, shape) * c_scale) + ctype, datasize = infer_datasize(dtype, shape) # Add padding, if any try: diff --git a/devito/ir/iet/visitors.py b/devito/ir/iet/visitors.py index 228a3e7f86..e9f2dd7e83 100644 --- a/devito/ir/iet/visitors.py +++ b/devito/ir/iet/visitors.py @@ -80,23 +80,24 @@ def indent(self): return ' ' * self._depth def visit_Node(self, o): - return self.indent + '<%s>' % o.__class__.__name__ + return self.indent + f'<{o.__class__.__name__}>' def visit_Generable(self, o): - body = ' %s' % str(o) if self.verbose else '' - return self.indent + '' % (o.__class__.__name__, body) + body = f" {str(o) if self.verbose else ''}" + return self.indent + f'' def visit_Callable(self, o): self._depth += 1 body = self._visit(o.children) self._depth -= 1 - return self.indent + '\n%s' % (o.name, body) + return self.indent + f'\n{body}' def visit_CallableBody(self, o): self._depth += 1 body = [self._visit(o.init), self._visit(o.unpacks), self._visit(o.body)] self._depth -= 1 - return self.indent + "%s\n%s" % (o.__repr__(), '\n'.join([i for i in body if i])) + cbody = '\n'.join([i for i in body if i]) + return self.indent + f"{o.__repr__()}\n{cbody}" def visit_list(self, o): return ('\n').join([self._visit(i) for i in o]) @@ -111,43 +112,46 @@ def visit_List(self, o): else: body = [self._visit(o.body)] self._depth -= 1 - return self.indent + "%s\n%s" % (o.__repr__(), '\n'.join(body)) + cbody = '\n'.join(body) + return self.indent + f"{o.__repr__()}\n{cbody}" def visit_TimedList(self, o): self._depth += 1 body = [self._visit(o.body)] self._depth -= 1 - return self.indent + "%s\n%s" % (o.__repr__(), '\n'.join(body)) + cbody = '\n'.join(body) + return self.indent + f"{o.__repr__()}\n{cbody}" def visit_Iteration(self, o): self._depth += 1 body = self._visit(o.children) self._depth -= 1 if self.verbose: - detail = '::%s::%s' % (o.index, o.limits) + detail = f'::{o.index}::{o.limits}' props = [str(i) for i in o.properties] - props = '[%s] ' % ','.join(props) if props else '' + cprops = ','.join(props) if props else '' + props = f'[{cprops}] ' else: detail, props = '', '' - return self.indent + "<%sIteration %s%s>\n%s" % (props, o.dim.name, detail, body) + return self.indent + f"<{props}Iteration {o.dim.name}{detail}>\n{body}" def visit_While(self, o): self._depth += 1 body = self._visit(o.children) self._depth -= 1 - return self.indent + "\n%s" % (o.condition, body) + return self.indent + f"\n{body}" def visit_Expression(self, o): if self.verbose: - body = "%s = %s" % (o.expr.lhs, o.expr.rhs) - return self.indent + "" % body + body = f"{o.expr.lhs} = {o.expr.rhs}" + return self.indent + f"" else: return self.indent + str(o) def visit_AugmentedExpression(self, o): if self.verbose: - body = "%s %s= %s" % (o.expr.lhs, o.op, o.expr.rhs) - return self.indent + "<%s %s>" % (o.__class__.__name__, body) + body = f"{o.expr.lhs} {o.op}= {o.expr.rhs}" + return self.indent + f"<{o.__class__.__name__} {body}>" else: return self.indent + str(o) @@ -155,7 +159,7 @@ def visit_HaloSpot(self, o): self._depth += 1 body = self._visit(o.children) self._depth -= 1 - return self.indent + "%s\n%s" % (o.__repr__(), body) + return self.indent + f"{o.__repr__()}\n{body}" def visit_Conditional(self, o): self._depth += 1 @@ -163,10 +167,9 @@ def visit_Conditional(self, o): self._depth -= 1 if o.else_body: else_body = self._visit(o.else_body) - return self.indent + "\n%s\n\n%s" % (o.condition, - then_body, else_body) + return self.indent + f"\n{then_body}\n\n{else_body}" else: - return self.indent + "\n%s" % (o.condition, then_body) + return self.indent + f"\n{then_body}" class CGen(Visitor): @@ -249,20 +252,20 @@ def _gen_value(self, obj, mode=1, masked=()): if (obj._mem_stack or obj._mem_constant) and mode == 1: strtype = self.ccode(obj._C_typedata) - strshape = ''.join('[%s]' % self.ccode(i) for i in obj.symbolic_shape) + strshape = ''.join(f'[{self.ccode(i)}]' for i in obj.symbolic_shape) else: strtype = self.ccode(obj._C_ctype) strshape = '' if isinstance(obj, (AbstractFunction, IndexedData)) and mode >= 1: if not obj._mem_stack: - strtype = '%s%s' % (strtype, self._restrict_keyword) + strtype = f'{strtype}{self._restrict_keyword}' strtype = ' '.join(qualifiers + [strtype]) if obj.is_LocalObject and obj._C_modifier is not None and mode == 2: strtype += obj._C_modifier strname = obj._C_name - strobj = '%s%s' % (strname, strshape) + strobj = f'{strname}{strshape}' if obj.is_LocalObject and obj.cargs and mode == 1: arguments = [self.ccode(i) for i in obj.cargs] @@ -386,16 +389,16 @@ def visit_PointerCast(self, o): if f.is_PointerArray: # lvalue - lvalue = c.Value(cstr, '**%s' % f.name) + lvalue = c.Value(cstr, f'**{f.name}') # rvalue if isinstance(o.obj, ArrayObject): - v = '%s->%s' % (o.obj.name, f._C_name) + v = f'{o.obj.name}->{f._C_name}' elif isinstance(o.obj, IndexedData): v = f._C_name else: assert False - rvalue = '(%s**) %s' % (cstr, v) + rvalue = f'({cstr}**) {v}' else: # lvalue @@ -404,12 +407,12 @@ def visit_PointerCast(self, o): else: v = f.name if o.flat is None: - shape = ''.join("[%s]" % self.ccode(i) for i in o.castshape) - rshape = '(*)%s' % shape + shape = ''.join(f"[{self.ccode(i)}]" for i in o.castshape) + rshape = f'(*){shape}' lvalue = c.Value(cstr, f'(*{self._restrict_keyword} {v}){shape}') else: rshape = '*' - lvalue = c.Value(cstr, '*%s' % v) + lvalue = c.Value(cstr, f'*{v}') if o.alignment and f._data_alignment: lvalue = c.AlignedAttribute(f._data_alignment, lvalue) @@ -422,14 +425,14 @@ def visit_PointerCast(self, o): else: assert False - rvalue = '(%s %s) %s->%s' % (cstr, rshape, f._C_name, v) + rvalue = f'({cstr} {rshape}) {f._C_name}->{v}' else: if isinstance(o.obj, Pointer): v = o.obj.name else: v = f._C_name - rvalue = '(%s %s) %s' % (cstr, rshape, v) + rvalue = f'({cstr} {rshape}) {v}' return c.Initializer(lvalue, rvalue) @@ -439,17 +442,19 @@ def visit_Dereference(self, o): i = a1.indexed cstr = self.ccode(i._C_typedata) if o.flat is None: - shape = ''.join("[%s]" % self.ccode(i) for i in a0.symbolic_shape[1:]) - rvalue = '(%s (*)%s) %s[%s]' % (cstr, shape, a1.name, - a1.dim.name) + shape = ''.join(f"[{self.ccode(i)}]" for i in a0.symbolic_shape[1:]) + rvalue = f'({cstr} (*){shape}) {a1.name}[{a1.dim.name}]' lvalue = c.Value(cstr, f'(*{self._restrict_keyword} {a0.name}){shape}') else: - rvalue = '(%s *) %s[%s]' % (cstr, a1.name, a1.dim.name) + rvalue = f'({cstr} *) {a1.name}[{a1.dim.name}]' lvalue = c.Value(cstr, f'*{self._restrict_keyword} {a0.name}') if a0._data_alignment: lvalue = c.AlignedAttribute(a0._data_alignment, lvalue) else: - rvalue = '*%s' % a1.name if a1.is_Symbol else '%s->%s' % (a1.name, a0._C_name) + if a1.is_Symbol: + rvalue = f'*{a1.name}' + else: + rvalue = f'{a1.name}->{a0._C_name}' lvalue = self._gen_value(a0, 0) return c.Initializer(lvalue, rvalue) @@ -494,7 +499,7 @@ def visit_Expression(self, o): def visit_AugmentedExpression(self, o): c_lhs = self.ccode(o.expr.lhs, dtype=o.dtype) c_rhs = self.ccode(o.expr.rhs, dtype=o.dtype) - code = c.Statement("%s %s= %s" % (c_lhs, o.op, c_rhs)) + code = c.Statement(f"{c_lhs} {o.op}= {c_rhs}") if o.pragmas: code = c.Module(self._visit(o.pragmas) + (code,)) return code @@ -538,23 +543,23 @@ def visit_Iteration(self, o): # For backward direction flip loop bounds if o.direction == Backward: - loop_init = 'int %s = %s' % (o.index, self.ccode(_max)) - loop_cond = '%s >= %s' % (o.index, self.ccode(_min)) - loop_inc = '%s -= %s' % (o.index, o.limits[2]) + loop_init = f'int {o.index} = {self.ccode(_max)}' + loop_cond = f'{o.index} >= {self.ccode(_min)}' + loop_inc = f'{o.index} -= {o.limits[2]}' else: - loop_init = 'int %s = %s' % (o.index, self.ccode(_min)) - loop_cond = '%s <= %s' % (o.index, self.ccode(_max)) - loop_inc = '%s += %s' % (o.index, o.limits[2]) + loop_init = f'int {o.index} = {self.ccode(_min)}' + loop_cond = f'{o.index} <= {self.ccode(_max)}' + loop_inc = f'{o.index} += {o.limits[2]}' # Append unbounded indices, if any if o.uindices: - uinit = ['%s = %s' % (i.name, self.ccode(i.symbolic_min)) for i in o.uindices] + uinit = [f'{i.name} = {self.ccode(i.symbolic_min)}' for i in o.uindices] loop_init = c.Line(', '.join([loop_init] + uinit)) ustep = [] for i in o.uindices: op = '=' if i.is_Modulo else '+=' - ustep.append('%s %s %s' % (i.name, op, self.ccode(i.symbolic_incr))) + ustep.append(f'{i.name} {op} {self.ccode(i.symbolic_incr)}') loop_inc = c.Line(', '.join([loop_inc] + ustep)) # Create For header+body @@ -577,7 +582,7 @@ def visit_While(self, o): return c.While(condition, c.Block(body)) else: # Hack: cgen doesn't support body-less while-loops, i.e. `while(...);` - return c.Statement('while(%s)' % condition) + return c.Statement(f'while({condition})') def visit_Callable(self, o): body = flatten(self._visit(i) for i in o.children) @@ -597,7 +602,7 @@ def visit_MultiTraversable(self, o): return c.Collection(body) def visit_UsingNamespace(self, o): - return c.Statement('using namespace %s' % str(o.namespace)) + return c.Statement(f'using namespace {str(o.namespace)}') def visit_Lambda(self, o): body = [] @@ -615,9 +620,11 @@ def visit_Lambda(self, o): extra.append(' '.join(str(i) for i in o.special)) if o.attributes: extra.append(' ') - extra.append(' '.join('[[%s]]' % i for i in o.attributes)) - top = c.Line('[%s](%s)%s' % - (', '.join(captures), ', '.join(decls), ''.join(extra))) + extra.append(' '.join(f'[[{i}]]' for i in o.attributes)) + ccapt = ', '.join(captures) + cdecls = ', '.join(decls) + cextra = ''.join(extra) + top = c.Line(f'[{ccapt}]({cdecls}){cextra}') return LambdaCollection([top, c.Block(body)]) def visit_HaloSpot(self, o): @@ -626,7 +633,8 @@ def visit_HaloSpot(self, o): def visit_KernelLaunch(self, o): if o.templates: - templates = '<%s>' % ','.join([str(i) for i in o.templates]) + ctemplates = ','.join([str(i) for i in o.templates]) + templates = f'<{ctemplates}>' else: templates = '' @@ -640,8 +648,7 @@ def visit_KernelLaunch(self, o): arguments = self._args_call(o.arguments) arguments = ','.join(arguments) - return c.Statement('%s%s<<<%s>>>(%s)' - % (o.name, templates, launch_config, arguments)) + return c.Statement(f'{o.name}{templates}<<<{launch_config}>>>({arguments})') # Operator-handle machinery @@ -742,7 +749,7 @@ class CInterface(CGen): def _operator_includes(self, o): includes = super()._operator_includes(o) - includes.append(c.Include("%s.h" % o.name, system=False)) + includes.append(c.Include(f"{o.name}.h", system=False)) return includes @@ -754,7 +761,7 @@ def visit_Operator(self, o): typedecls = self._operator_typedecls(o, mode='public') guarded_typedecls = [] for i in typedecls: - guard = "DEVITO_%s" % i.tpname.upper() + guard = f"DEVITO_{i.tpname.upper()}" iflines = [c.Define(guard, ""), blankline, i, blankline] guarded_typedecl = c.IfNDef(guard, iflines, []) guarded_typedecls.extend([guarded_typedecl, blankline]) @@ -1383,13 +1390,15 @@ def __init__(self, name, arguments, is_expr=False, is_indirect=False, def generate(self): if self.templates: - tip = "%s<%s>" % (self.name, ", ".join(str(i) for i in self.templates)) + ctemplates = ", ".join(str(i) for i in self.templates) + tip = f"{self.name}<{ctemplates}>" else: tip = self.name if not self.is_indirect: - tip = "%s(" % tip + tip = f"{tip}(" else: - tip = "%s%s" % (tip, ',' if self.arguments else '') + cargs = ',' if self.arguments else '' + tip = f"{tip}{cargs}" processed = [] for i in self.arguments: if isinstance(i, (MultilineCall, LambdaCollection)): @@ -1411,7 +1420,7 @@ def generate(self): if not self.is_expr: tip += ";" if self.cast: - tip = '(%s)%s' % (self.cast, tip) + tip = f'({self.cast}){tip}' yield tip diff --git a/devito/mpi/routines.py b/devito/mpi/routines.py index 67158a621c..39a4554296 100644 --- a/devito/mpi/routines.py +++ b/devito/mpi/routines.py @@ -16,7 +16,7 @@ from devito.mpi import MPI from devito.symbolics import (Byref, CondNe, FieldFromPointer, FieldFromComposite, IndexedPointer, Macro, cast_mapper, subs_op_args) -from devito.tools import (as_mapper, dtype_to_mpitype, dtype_len, dtype_alloc_ctype, +from devito.tools import (as_mapper, dtype_to_mpitype, dtype_len, infer_datasize, flatten, generator, is_integer, split) from devito.types import (Array, Bag, Dimension, Eq, Symbol, LocalObject, CompositeObject, CustomDimension) @@ -1204,8 +1204,8 @@ def _arg_defaults(self, allocator, alias, args=None): entry.sizes = (c_int*len(shape))(*shape) # Allocate the send/recv buffers - ctype, c_scale = dtype_alloc_ctype(f.dtype) - size = int(reduce(mul, shape) * c_scale) * dtype_len(self.target.dtype) + ctype, datasize = infer_datasize(f.dtype, shape) + size = datasize * dtype_len(self.target.dtype) entry.bufg, bufg_memfree_args = allocator._alloc_C_libcall(size, ctype) entry.bufs, bufs_memfree_args = allocator._alloc_C_libcall(size, ctype) diff --git a/devito/tools/dtypes_lowering.py b/devito/tools/dtypes_lowering.py index e1bfe481a0..867302faa1 100644 --- a/devito/tools/dtypes_lowering.py +++ b/devito/tools/dtypes_lowering.py @@ -3,6 +3,8 @@ """ import ctypes +from functools import reduce +from operator import mul import numpy as np from cgen import dtype_to_ctype as cgen_dtype_to_ctype @@ -11,7 +13,7 @@ __all__ = ['int2', 'int3', 'int4', 'float2', 'float3', 'float4', 'double2', # noqa 'double3', 'double4', 'dtypes_vector_mapper', 'dtype_to_mpidtype', - 'dtype_to_cstr', 'dtype_to_ctype', 'dtype_alloc_ctype', 'dtype_to_mpitype', + 'dtype_to_cstr', 'dtype_to_ctype', 'infer_datasize', 'dtype_to_mpitype', 'dtype_len', 'ctypes_to_cstr', 'c_restrict_void_p', 'ctypes_vector_mapper', 'is_external_ctype', 'infer_dtype', 'CustomDtype'] @@ -152,12 +154,13 @@ def dtype_to_ctype(dtype): return np.ctypeslib.as_ctypes_type(dtype) -def dtype_alloc_ctype(dtype): +def infer_datasize(dtype, shape): """ Translate numpy.dtype to (ctype, int): type and scale for correct C allocation size. """ + datasize = int(reduce(mul, shape)) if isinstance(dtype, CustomDtype): - return dtype, 1 + return dtype, datasize try: return ctypes_vector_mapper[dtype], 1 @@ -165,17 +168,17 @@ def dtype_alloc_ctype(dtype): pass if issubclass(dtype, ctypes._SimpleCData): - return dtype, 1 + return dtype, datasize if dtype == np.float16: # Allocate half float as unsigned short - return ctypes.c_uint16, 1 + return ctypes.c_uint16, datasize if np.issubdtype(dtype, np.complexfloating): # For complex float, allocate twice the size of real/imaginary part - return np.ctypeslib.as_ctypes_type(dtype(0).real.__class__), 2 + return np.ctypeslib.as_ctypes_type(dtype(0).real.__class__), 2 * datasize - return np.ctypeslib.as_ctypes_type(dtype), 1 + return np.ctypeslib.as_ctypes_type(dtype), datasize def dtype_to_mpitype(dtype): diff --git a/tests/test_dtypes.py b/tests/test_dtypes.py index 5d31fd2a21..fad1840383 100644 --- a/tests/test_dtypes.py +++ b/tests/test_dtypes.py @@ -131,7 +131,7 @@ def test_complex_headers(dtype: np.dtype[np.inexact], kwargs: dict[str, str]) -> op = Operator(eq, **kwargs) # Check that the complex header is included <=> complex dtypes are present - header: str = _get_language(**kwargs).get('header-complex') + header: str = _get_language(**kwargs).get('includes-complex') if np.issubdtype(dtype, np.complexfloating): assert header in op._includes else: