[ET Trac] #2916: Include Cottonmouth
Zach Etienne
trac-noreply at einsteintoolkit.org
Thu Jun 4 09:42:19 CDT 2026
#2916: Include Cottonmouth
Reporter: Beyhan Karakaş
Status: open
Milestone: ET_2026_05
Version:
Type: enhancement
Priority: major
Component: EinsteinToolkit thorn
Comment (by Zach Etienne):
Here is an updated list of issues, focusing on EinsteinEngine (the generator for Cottonmouth & Friends):
```
Only issues with confidence greater than 90% are included. The list is ordered by confidence, highest first.
* Issue 1
- Severity critical
- Description of the error: The checked-in 4D Kerr recipe uses `dimensionality=4`, but derivative handling is hard-coded to three axes. Running the recipe reaches `l3` and fails with `Exception: Bad index passed to derivative: x: idx=l3`.
- Filename + line number(s): `recipes/compute-kerr.py:26-73`, `EinsteinEngine/frontend/dsl/finite_difference.py:108-143`, `EinsteinEngine/frontend/dsl/finite_difference.py:337-405`
- Original code snippet
```python
elif idx == L2:
if expr == z:
return one
elif expr in [x, y]:
return zero
elif expr in self.params:
return zero
else:
raise Exception(f"Bad index passed to derivative: {expr}: idx={idx}")
```
- Fixed code snippet.
```python
# DivMakerVisitor
def set_coords(self, coords: list[Symbol]) -> None:
self.coords = coords
self.idx_map = {mk_idx(f"l{i}"): coord for i, coord in enumerate(coords)}
@visit.register
def _(self, expr: sy.Symbol, idx: sy.Idx) -> Expr:
if idx is no_idx:
return expr
if idx in self.idx_map:
coord = self.idx_map[idx]
if expr == coord:
return one
if expr in self.coords or expr in self.params:
return zero
return _mk_div(self.div_func, expr, idx)
raise DslException(f"Unsupported derivative index {idx}")
# DslFrontend.mk_coords(), after self.coords is finalized:
for dmv in self.div_makers.values():
dmv.set_coords(self.coords)
```
- Confidence: 99.8%
* Issue 2
- Severity high
- Description of the error: `sech()` and `csch()` derivatives use the global symbol `x` instead of the differentiated function argument, silently producing wrong symbolic equations for non-`x` operands.
- Filename + line number(s): `EinsteinEngine/frontend/dsl/finite_difference.py:63-78`
- Original code snippet
```python
sy.sech: lambda self, r, idx: -sech(r) * tanh(x) * self.visit(r, idx),
sy.csch: lambda self, r, idx: -csch(r) * coth(x) * self.visit(r, idx),
```
- Fixed code snippet.
```python
sy.sech: lambda self, r, idx: -sech(r) * tanh(r) * self.visit(r, idx),
sy.csch: lambda self, r, idx: -csch(r) * coth(r) * self.visit(r, idx),
```
- Confidence: 99.8%
* Issue 3
- Severity high
- Description of the error: Cactus keyword and explicit numeric range parameters fail because runtime values are compared to parameterized generic aliases such as `set[str]` and `tuple[int, int]`.
- Filename + line number(s): `EinsteinEngine/generators/cpp_carpetx_generator.py:535-580`
- Original code snippet
```python
assert type(py_param_range) is set[str]
...
assert type(py_param_range) is tuple[int, int]
...
assert type(py_param_range) is tuple[float, float]
```
- Fixed code snippet.
```python
if py_param_type is set:
if not isinstance(py_param_range, set) or not all(isinstance(s, str) for s in py_param_range):
raise GeneratorException(f"Bad keyword range for parameter {param_name}")
param_range = KeywordParamRange([String(s) for s in sorted(py_param_range)], String(""))
elif py_param_type is int:
if py_param_range is None:
param_range = IntParamRange(IntParamDescWildcard(), String(""))
elif (
isinstance(py_param_range, tuple)
and len(py_param_range) == 2
and all(type(v) is int for v in py_param_range)
):
lo_i, hi_i = py_param_range
param_range = IntParamRange(IntParamDescRange(
IntParamOpenLowerBound(Integer(lo_i)),
IntParamOpenUpperBound(Integer(hi_i))
), String(""))
else:
raise GeneratorException(f"Bad integer range for parameter {param_name}")
elif py_param_type is float:
if py_param_range is None:
param_range = RealParamRange(RealParamDescWildcard(), String(""))
elif (
isinstance(py_param_range, tuple)
and len(py_param_range) == 2
and all(isinstance(v, (int, float)) for v in py_param_range)
):
lo_f, hi_f = map(float, py_param_range)
param_range = RealParamRange(RealParamDescRange(
RealParamOpenLowerBound(Float(lo_f)),
RealParamOpenUpperBound(Float(hi_f))
), String(""))
else:
raise GeneratorException(f"Bad real range for parameter {param_name}")
```
- Confidence: 99.5%
* Issue 4
- Severity high
- Description of the error: Vanilla F90 generation emits empty declarations and empty deallocation calls for functions without local temporaries or tile temporaries, producing invalid Fortran such as `DOUBLE PRECISION ::` and `DEALLOCATE()`.
- Filename + line number(s): `EinsteinEngine/generators/vanilla_f90_generator.py:289-302`, `EinsteinEngine/generators/vanilla_f90_generator.py:373-415`, `EinsteinEngine/emit/code/f90/f90_visitor.py:149-152`, `EinsteinEngine/emit/code/f90/f90_visitor.py:207-208`
- Original code snippet
```python
loop_body.append(VarDecl(
type=TypeSpecifier(
type=PrimitiveType.Double,
attributes=[]
),
names=[Identifier(temp_name) for temp_name in local_temporaries]
))
deallocate_stmt = ExprStmt(FunctionCall(Identifier('DEALLOCATE'), list(grid_temp_deallocs), []))
...
destructs=[
deallocate_stmt
]
```
- Fixed code snippet.
```python
if local_temporaries:
loop_body.append(VarDecl(
type=TypeSpecifier(type=PrimitiveType.Double, attributes=[]),
names=[Identifier(temp_name) for temp_name in local_temporaries]
))
destructs: list[F90StmtNode] = []
if grid_temp_deallocs:
destructs.append(ExprStmt(FunctionCall(
Identifier("DEALLOCATE"),
list(grid_temp_deallocs),
[]
)))
...
destructs=destructs
```
- Confidence: 99.0%
* Issue 5
- Severity medium
- Description of the error: `ScheduleBlock.if_var` renders as `IN <var>` instead of `IF <var>`, producing malformed or semantically wrong Cactus schedule CCL for direct `if_var` users.
- Filename + line number(s): `EinsteinEngine/emit/ccl/schedule/schedule_visitor.py:83-87`
- Original code snippet
```python
if n.if_var is not None:
s += f' IN {self.visit(n.if_var)}'
```
- Fixed code snippet.
```python
if n.if_var is not None:
s += f' IF {self.visit(n.if_var)}'
```
- Confidence: 99.0%
* Issue 6
- Severity medium
- Description of the error: Non-integer stencil offsets are silently truncated by `int(expr.evalf())`, so malformed offsets like `1/2` become `0` instead of being rejected.
- Filename + line number(s): `EinsteinEngine/emit/code/sympy_visitor.py:150-156`
- Original code snippet
```python
@staticmethod
def _to_stencil_offset(v: sy.Basic) -> int:
if isinstance(v, sy.Integer):
return int(v)
# noinspection PyTypeChecker
return int(cast(sy.Expr, v).evalf()) # type: ignore[no-untyped-call]
```
- Fixed code snippet.
```python
@staticmethod
def _to_stencil_offset(v: sy.Basic) -> int:
if isinstance(v, sy.Integer):
return int(v)
raise DslException(f"Stencil offset must be an integer, got {v!s}")
```
- Confidence: 99.0%
* Issue 7
- Severity medium
- Description of the error: Ghost-zone limit discovery misses equations whose RHS is exactly a stencil call because `_stencil_limits()` checks only child arguments and not the root expression.
- Filename + line number(s): `EinsteinEngine/intermediate/eqnlist.py:1215-1230`
- Original code snippet
```python
def _stencil_limits(self, result: List[int], expr: Expr) -> None:
for arg in expr.args:
if str(type(arg)) == "stencil":
for i in range(3):
ivar = arg.args[i + 1]
assert isinstance(ivar, Integer), f"ivar={ivar}, type={type(ivar)}"
result[i] = max(result[i], abs(int(ivar)))
else:
if isinstance(arg, Expr):
self._stencil_limits(result, arg)
```
- Fixed code snippet.
```python
def _stencil_limits(self, result: List[int], expr: Expr) -> None:
if getattr(expr, "func", None) is not None and self.is_stencil.get(expr.func, False):
if len(expr.args) != 4:
raise DslException(f"Stencil call should have 4 arguments: {expr}")
for i in range(3):
ivar = expr.args[i + 1]
if not isinstance(ivar, Integer):
raise DslException(f"Stencil offset must be an integer: {expr}")
result[i] = max(result[i], abs(int(ivar)))
return
for arg in expr.args:
if isinstance(arg, Expr):
self._stencil_limits(result, arg)
```
- Confidence: 98.5%
* Issue 8
- Severity medium
- Description of the error: Duplicate equation detection uses `assert`, so `python -O` disables the guard and lets a second equation silently overwrite the first.
- Filename + line number(s): `EinsteinEngine/intermediate/eqnlist.py:668-672`
- Original code snippet
```python
def add_eqn(self, lhs: Symbol, rhs: Expr) -> None:
assert lhs not in self.eqns, f"Equation for '{lhs}' is already defined"
# Ensure we only have symbols in eqnlist
self.eqns[lhs := symbify(lhs)] = symbify(rhs)
self.eqn_insertion_order[lhs] = len(self.eqns) - 1
```
- Fixed code snippet.
```python
def add_eqn(self, lhs: Symbol, rhs: Expr) -> None:
lhs = symbify(lhs)
if lhs in self.eqns:
raise IntermediateException(f"Equation for '{lhs}' is already defined")
self.eqns[lhs] = symbify(rhs)
self.eqn_insertion_order[lhs] = len(self.eqns) - 1
```
- Confidence: 98.5%
* Issue 9
- Severity medium
- Description of the error: Invalid tensor symmetry declarations are not reliably rejected. The second missing-index check compares `i2` against `-2` even though the sentinel is `-1`, and all validation is assert-based.
- Filename + line number(s): `EinsteinEngine/frontend/dsl/dsl_frontend.py:802-815`
- Original code snippet
```python
assert i1 != -1, f"Index {ix1} not in {tens}"
assert i2 != -2, f"Index {ix2} not in {tens}"
assert i1 != i2, f"Index {ix1} cannot be symmetric with itself in {tens}"
```
- Fixed code snippet.
```python
if i1 == -1:
raise DslException(f"Index {ix1} not in {tens}")
if i2 == -1:
raise DslException(f"Index {ix2} not in {tens}")
if i1 == i2:
raise DslException(f"Index {ix1} cannot be symmetric with itself in {tens}")
```
- Confidence: 98.0%
* Issue 10
- Severity high
- Description of the error: The overwrite/stencil safety guard never detects stencil calls because it checks for the string `"stencil"` inside `rhs.free_symbols`, which contains SymPy symbols, not function names.
- Filename + line number(s): `EinsteinEngine/intermediate/eqnlist.py:963-971`, `EinsteinEngine/generators/cpp_carpetx_generator.py:932-935`, `EinsteinEngine/generators/vanilla_f90_generator.py:284-287`
- Original code snippet
```python
for rhs in self.eqns.values():
if "stencil" in rhs.free_symbols:
raise DslException(f"Overwrite source symbol {rd} cannot be used inside a stencil")
```
- Fixed code snippet.
```python
for rd in rd_overwrites:
for rhs in self.eqns.values():
for call in rhs.find(lambda e: hasattr(e, "func") and self.is_stencil.get(e.func, False)):
if len(call.args) > 0 and call.args[0] == rd:
raise DslException(
f"Overwrite source symbol {rd} cannot be used inside a stencil"
)
```
- Confidence: 97.0%
* Issue 11
- Severity medium
- Description of the error: Package metadata does not enforce the documented Python 3.13 minimum, so unsupported interpreters can install the package and fail later.
- Filename + line number(s): `README.md:5-7`, `setup.py:20-40`
- Original code snippet
```python
setup(
name='EinsteinEngine',
version='0.1.0',
...
install_requires=[
...
]
)
```
- Fixed code snippet.
```python
setup(
name='EinsteinEngine',
version='0.1.0',
python_requires='>=3.13',
...
install_requires=[
...
]
)
```
- Confidence: 97.0%
* Issue 12
- Severity medium
- Description of the error: Generated CarpetX thorns always require NewRadX and include `newradx.hxx`, even when no NewRadX boundary functions are used.
- Filename + line number(s): `EinsteinEngine/wizards/thorn_wizards.py:90-99`, `EinsteinEngine/generators/cpp_carpetx_generator.py:510-518`
- Original code snippet
```python
configuration_ccl = f"""
REQUIRES Arith Loop {self.thorn_def.name}_gen AMReX NewRadX
...
""".strip()
```
```python
IncludeSection([
UsesInclude(Verbatim('newradx.hxx'))
]),
```
- Fixed code snippet.
```python
requires = f"REQUIRES Arith Loop {self.thorn_def.name}_gen AMReX"
if self.generator.options.get("new_rad_x_boundary_fns"):
requires += " NewRadX"
configuration_ccl = f"""
{requires}
PROVIDES {self.thorn_def.name}_gen
{{
# SCRIPT bin/generate.py
# LANG python3
}}
""".strip()
```
```python
includes = []
if self.options.get("new_rad_x_boundary_fns"):
includes.append(UsesInclude(Verbatim("newradx.hxx")))
IncludeSection(includes),
```
- Confidence: 96.0%
* Issue 13
- Severity medium
- Description of the error: Vanilla F90 module parameter declarations are flattened from per-function maps without deduplicating by parameter name, so multiple functions using the same parameter can declare it more than once.
- Filename + line number(s): `EinsteinEngine/generators/vanilla_f90_generator.py:446-458`
- Original code snippet
```python
param_decls: list[VarDecl] = list(
VarDecl(
type=TypeSpecifier(
type=param.get_type(),
attributes=[Public()]
),
names=[Identifier(var)],
) for var, param in flatten(self.params[fn_name].items() for fn_name in fn_names)
)
```
- Fixed code snippet.
```python
module_params: OrderedDict[str, CactusParam] = OrderedDict()
for fn_name in fn_names:
for var, param in self.params[fn_name].items():
module_params.setdefault(var, param)
param_decls: list[VarDecl] = [
VarDecl(
type=TypeSpecifier(type=param.get_type(), attributes=[Public()]),
names=[Identifier(var)],
)
for var, param in module_params.items()
]
```
- Confidence: 95.0%
* Issue 14
- Severity medium
- Description of the error: Reciprocal trig/hyperbolic functions are accepted by the SymPy visitor and emitted directly as `cot`, `sec`, `csc`, `coth`, `sech`, and `csch`, which are not standard C++ functions and are not Fortran intrinsics in the emitted form.
- Filename + line number(s): `EinsteinEngine/emit/code/sympy_visitor.py:38-50`, `EinsteinEngine/emit/code/cpp_carpetx/cpp_carpetx_visitor.py:42-54`, `EinsteinEngine/emit/code/f90/f90_visitor.py:39-51`, `EinsteinEngine/generators/cpp_carpetx_generator.py:111-112`
- Original code snippet
```python
sy.cot: StandardizedFunctionCallType.Cot,
sy.sec: StandardizedFunctionCallType.Sec,
sy.csc: StandardizedFunctionCallType.Csc,
...
StandardizedFunctionCallType.Cot: 'cot',
StandardizedFunctionCallType.Sec: 'sec',
StandardizedFunctionCallType.Csc: 'csc',
```
- Fixed code snippet.
```python
reciprocal_rewrites: dict[sy.Function, Callable[
--
Ticket URL: https://bitbucket.org/einsteintoolkit/tickets/issues/2916/include-cottonmouth
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.einsteintoolkit.org/pipermail/trac/attachments/20260604/49a1ebe3/attachment.htm>
More information about the Trac
mailing list