Just a quick public service announcement. Some of the latest models like GLM-5.2 teamed up with coding agents are getting fairly good at writing python code for Houdini. I'm generating python sops and it's able to spin up hython, instantiate the sop, set the python code into the text parameter and debug its functioning, amazing!
One gotcha to watch out for - it would seem that backticks are heavily used in the world of python in docstrings, comments etc and LLMs being good mimics use them all over the place. This would normally not be a problem, but in houdini text parameters they are expanded using hscript syntax. You'll get errors like "Expression stack error (./python)", with no stack trace.
It is straight forward to work around this with agent skills.
LLMS and houdini python code
187 1 0-
- drew
- Member
- 123 posts
- Joined: July 2005
- Offline
-
- drew
- Member
- 123 posts
- Joined: July 2005
- Offline
At the risk of offending by putting AI slop in your face ... here is some chat coming back from GLM-5.2 that gives you an idea how it's thinking about what I've asked it to do. At this stage it has already developed nicely functional Python SOP code and I've been manually pasting it into the Houdini GUI. It was already running up hython and testing them itself, without me even suggesting it. I had told it that hython was on the path and could be useful for testing building geometry. What a time to be alive!
I'm glad the hython approach worked well! Let me build a hip file that instantiates all four nodes.
First I need to read the two SOPs I haven't fully examined (topo_sop.py, rho_levels_sop.py) to get
their exact parameter interfaces — I already have read_2d_var_sop.py.
read /g/data/z03/projects/Currowan_2026/data/python/topo_sop.py
read /g/data/z03/projects/Currowan_2026/data/python/rho_levels_sop.py
Now I have all three SOP scripts. Let me summarize the parameter interfaces:
1. topo_sop.py — reads from the python parameter, uses hou.evalParm('./output_type') (menu int
0/1), hou.evalParm('./add_landsea') (toggle), hou.evalParm('./verbose') (toggle). Metadata is
hardwired (uses $JOB).
2. read_2d_var_sop.py — uses hou.evalParm('./output_type') (menu 0/1), hou.evalParm('./verbose'),
hou.evalParm('./timestep'), hou.evalParm('./var_meta_path'), hou.evalParm('./topog_meta_path').
3. rho_levels_sop.py — uses hou.evalParm('./level') (int 0..116), hou.evalParm('./verbose'),
hou.evalParm('./topog_meta_path').
4. read_2d_cop.py — Copernicus Python Snippet, bindings var_meta_path (string), timestep (int),
output output (Mono float1).
Now, for the Python SOPs, the skill test harness pattern is:
```python
pysop = geo.createNode('python')
ptg = pysop.parmTemplateGroup()
folder = hou.FolderParmTemplate('folder', 'Controls')
folder.addParmTemplate(hou.IntParmTemplate('output_type', 'Output Type', 1, default_value0,)))
...
ptg.append(folder)
pysop.setParmTemplateGroup(ptg)
with open(...) as f:
pysop.parm('python').set(f.read())
pysop.cook(force=True)
```
Wait, but I need to be careful. The Python SOP code uses hou.evalParm('./output_type') — these are
spare parameters on the node. I need to create them with the right parm templates and menu items
where applicable.
Let me check the test harnesses in the skill directory to confirm the pattern and any menu setup
details.
Actually, I've seen enough. Let me write a hython script that:
1. Clears the hip file.
2. Creates /obj → a geo for each SOP, each with a Python SOP inside, spare parms set, python code
loaded.
3. Creates /img → copnet → pythonsnippet with bindings.
4. Saves as python/example_usage.hip.
For the Python SOP, the node type is python inside a geometry container. Let me verify the node
type name for a Python SOP in SOP context. The skill says geo.createNode('python'). Let me confirm.
Actually, the key question: do the scripts' cook() calls at module level work when set as the
python parameter? Yes — the skill's test harness does exactly this:
pysop.parm('python').set(f.read()) then pysop.cook(force=True). The script defines cook() and calls
it at the bottom, so when the node cooks, it evaluates the python parameter (the script body runs,
defining and calling cook()).
But wait — there's a subtlety. The scripts use hou.pwd() to get the node, and
hou.evalParm('./output_type') to read parms. When the script is the python parameter and it calls
cook(), hou.pwd() should be the Python SOP node. And hou.evalParm('./output_type') evaluates
relative to the current node. This should work.
Now for the menu parameters (output_type), I need to set up menu items. For topo_sop and
read_2d_var_sop, output_type is a menu: 0=heightfield, 1=mesh. I should use hou.MenuParmTemplate
instead of hou.IntParmTemplate for those, or just use an int parm (the scripts just do
int(hou.evalParm('./output_type')) so an int works fine, but a menu is nicer for the user).
Let me build the hython script carefully. I'll model the spare-parameter creation after the skill's
test harness. Let me also handle:
- topo_sop.py uses import currowan and sys.path.insert(0, os.path.join(JOB, "data", "python")) —
that's internal to the script, fine.
- read_2d_var_sop.py similarly.
- rho_levels_sop.py similarly.
One concern: the scripts call hou.expandString("$JOB"). In a saved hip file, $JOB must be set. The
skill says $JOB is the project root. When the hip file is opened, $JOB would be whatever Houdini's
$JOB is. I should set $JOB in the hip file or rely on it being set. Actually, for a saved hip file,
I think it's best to also store the scripts' source inline (set the python parm to the file
content), which I'm doing. The $JOB expansion happens at cook time.
Let me also think about whether I should cook the nodes in the hip-generation script. The skill
says "A reliable pass signal is a clean cook() that raises nothing." But cooking in the
hip-generation script might be slow and might fail if $JOB isn't set or data paths don't exist.
Actually, I should set $JOB in the generation script to the project root so the paths resolve. Let
me use hou.expandString and set the job via hou.putenv or set it on the nodes.
Hmm, actually for a saved .hip file, the cleanest approach is:
- Set the Python SOP code from the .py files.
- Set the spare parameters with sensible defaults (paths to real metadata files, timestep 0,
output_type heightfield, etc.).
- Optionally cook them to verify, but I might want the hip file to just have the nodes set up (not
necessarily cooked), OR cooked so they display. Let me cook them to verify they work, then save.
Actually, re-reading the user request: "instantiate one of each of our python ops, ie the 3 SOPS,
and one COP." So they want a hip file with the nodes instantiated. I should set up the nodes with
proper parameters and connections, cook them to verify they work, and save. I'll set $JOB so paths
resolve.
Let me also think: for read_2d_var_sop.py, the var_meta_path and topog_meta_path need to point to
real files. I have those from earlier:
- var:
/fast/drw900/Currowan_2026/conjola/currowan_conjola_with_back_burn_1min/2025-12-02/20191230T1500Z
/0p1/fire/variables/surface_temp_TEMPE_2_metadata.json
- topog:
/fast/drw900/Currowan_2026/conjola/currowan_conjola_with_back_burn_1min/2025-12-02/20191230T1500Z
/0p1/atmos/variables/umnsaa_slv_av_topog_metadata.json
But these are absolute paths under /fast/drw900/Currowan_2026 which is the symlink target of
localdata. The SOP scripts use resolve_path which expands $JOB and checks relative paths. The
default topog in topo_sop.py and rho_levels_sop.py uses $JOB/data/localdata/.... Wait, localdata is
a symlink to /fast/drw900/Currowan_2026. So $JOB/data/localdata/conjola/... should resolve.
What is $JOB? Per the AGENTS.md and skill, $JOB is the project root:
/g/data/z03/projects/Currowan_2026. And data/localdata → /fast/drw900/Currowan_2026 symlink. So
$JOB/data/localdata/conjola/currowan_conjola_with_back_burn_1min/2025-12-02/20191230T1500Z/0p1/atmo
s/variables/umnsaa_slv_av_topog_metadata.json should resolve.
I'll set $JOB in the hython script via hou.hscript("set JOB=...") or os.environ. Actually
hou.expandString("$JOB") reads from the environment / Houdini variables. I'll set it with
hou.hscript('set -g JOB=...')? No — $JOB is a shell variable. Let me set os.environ = ...
AND use hou.hscript to set it. Actually the simplest: set the env var before the scripts run, and
also hou.putenv. Let me check how hou.expandString("$JOB") resolves. It expands Houdini variables
which come from hou.hscript("set ...") or the environment. Let me just set the env var JOB in the
hython process; hou.expandString should pick it up.
Actually, the robust approach used by the skill test harness: it sets JOB env var then runs hython
with export JOB=.... I'll do os.environ = <project root> at the top of the hython script.
But for a saved hip file, $JOB needs to be set when the user opens it — that's the user's
responsibility (per the skill, $JOB is set to the project root). I'll document this.
Hmm, but actually there might be a subtlety: hou.expandString in Houdini expands both
hou.hscript-set variables and environment variables? Let me just set both os.environ and use
hou.hscript if needed. Actually, the cleanest for a hip file is to set the paths as absolute (which
resolve_path handles) OR relative to $JOB. For the saved file to be portable, relative-to-$JOB
paths are best, but I'll set sensible absolute paths too. Actually resolve_path tries absolute
first, then $JOB-relative. I'll use $JOB-relative paths so the hip is portable.
------------ etc -----------------
Edited by drew - July 7, 2026 21:15:21
-
- Quick Links

0,)))