sofia.ir.extension¶
Extension points for code that runs inside generated programs.
Plugins add runtime behaviour by subclassing KernelExtension (a pointwise
function inside a kernel expression) or ProgramExtension (a statement in the
generated main). Nothing is registered: each code printer has one generic
print method for each base class, which delegates to the instance. Importing a
plugin defines its classes; using it means putting instances in the IR.
Both classes name the modules their generated code needs in runtime_imports.
These are import lines added to the generated module, so plugin code never
imports its own Warp code.
- class sofia.ir.extension.KernelExtension(*args)¶
Bases:
FunctionBase class for a pointwise function inside a kernel expression.
A subclass is used like any sympy function, e.g.
chi << InsideBody(geo, x, y, z), and appears in kernel expressions. Its code is printed by the method named after the kernel target, which receives the arguments already printed and returns the printed call:warp(self, *args: str) -> strfor Warp kernelsbackend(self, *args: str) -> strfor backend (numpy / jax) kernels
A subclass implements one or both; codegen picks a kernel target the subclass implements. Arguments are sympified on construction, as for any sympy function, so codegen can traverse and cache expressions containing them.
- runtime_imports¶
Import lines the printed code needs, added to the generated module.
- Type:
ClassVar[tuple[str, …]]
Examples
```python from sofia import ir
- class InsideBody(ir.KernelExtension):
- runtime_imports = (
“from sofia_contrib_kernels.ibm.kernels import inside_body”,
)
- def warp(self, geo, x, y, z):
return f”inside_body({geo}, {x}, {y}, {z})”
- class sofia.ir.extension.ProgramExtension(*args, **kwargs)¶
Bases:
CodegenTokenBase class for a statement in the generated program.
A subclass is an IR token, with fields declared as for
CodegenToken, and is placed in anir.CodeBlocklike any other statement. Codegen callsemitwith aProgramContext, which adds kernels and functions to the generated module, and puts the returned statement inmain.All fields must hold sympy objects, so that the token can be traversed, substituted and cached by hash. Use a converter for other values, e.g.
maxiter: sp.Integer = field(converter=sp.sympify, default=8).- runtime_imports¶
Import lines the emitted code needs, added to the generated module.
- Type:
ClassVar[tuple[str, …]]
Examples
```python from sofia import Field, Geometry, ir
- class GhostCellFill(ir.ProgramExtension):
- runtime_imports = (
“from sofia_contrib_kernels.ibm.kernels import ghost_value”,
)
field: Field geometry: Geometry
@property def inputs(self):
return {self.field, self.geometry}
@property def outputs(self):
return {self.field}
- def emit(self, ctx):
geo, f = ctx.print_in_kernel(self.geometry), ctx.print_in_kernel(self.field) return ctx.warp_kernel(
output=self.field, inputs=(self.geometry, self.field), body=lambda out: (
“i, j, k = warp.tid()n” f”{out}[i, j, k] = ghost_value({geo}, {f}, i, j, k)”
),
)
- abstract property inputs: set¶
Fields, scalars and geometries the statement reads.
- abstract property outputs: set¶
Fields and scalars the statement writes.
- abstractmethod emit(ctx: ProgramContext) str¶
Return the statement for
main, adding what it calls throughctx.
- class sofia.ir.extension.ProgramContext(*args, **kwargs)¶
Bases:
ProtocolWhat
ProgramExtension.emitcan add to the generated module.Each method that adds code returns the code that uses it, to be put in the statement
emitreturns.- print_in_kernel(expr: Basic) str¶
Print
expras it appears in a Warp kernel, e.g. a field’s array.Use it for code in the body given to
warp_kernel.
- print_in_program(expr: Basic) str¶
Print
expras it appears in the program, outside any kernel.E.g. a field’s variable, or the unique name of an
sp.Dummy. Use it for the statementemitreturns and the body given tofunction.
- add_warp_func(expr: Basic, args: Iterable[Symbol] | None = None) str¶
Compile
exprinto a@warp.funcand return the function’s name.expris either a scalar expression, compiled to a function offloat64arguments, or ansp.Tupleof 2 or 3 components, each in one symbol, compiled to a function of onevec2dorvec3d.argssets a scalar function’s arguments, in order (default: the symbols inexpr, in no fixed order), so e.g. a constant still takes them and returnsfloat64.
- warp_kernel(output: Basic, inputs: Iterable[Basic], body: Callable[[str], str], assign: bool = True) str¶
Add a
@warp.kernelthat computesoutputand return its call.body(out)returns the kernel body, given the name of the array the kernel writes. That array is separate fromoutput’s input array, so the body reads the old values ofoutput. The returned statement assigns the result tooutput; withassign=False, it is only the call, e.g. to assign to a temporary named withprint_in_program.
- function(body: str, inputs: Iterable[Basic], outputs: Iterable[Basic]) str¶
Wrap the statements
bodyin a backend function and return its call.The function takes
inputsand returnsoutputs, which the returned statement assigns.