# Architecture ## Overview `ifccommit.py` is a single-file CLI tool for slicing and composing IFC files. It dispatches to one of twelve commands via `argparse` subparsers. ``` main() ├── cmd_list — inspect ├── cmd_info — inspect ├── cmd_extract — extract → one output file ├── cmd_insert — merge → one output file ├── cmd_replace — remove space + merge part → one output file ├── cmd_split — extract → one output file per storey ├── cmd_space — extract space + contents → one output file ├── cmd_move — translate element → one output file ├── cmd_copy — copy elements and translate → one output file ├── cmd_diff — diff two IFC files → stdout or file ├── cmd_remove — remove elements by type → one output file ├── cmd_history — write or read Pset_GitCommit on IFC elements └── cmd_run — fetch and execute yaml pipeline from demo repo ``` ## Commands | Command | Input(s) | Output | Description | |-----------|-----------------------------------|-----------------|----------------------------------------------------| | `list` | `input.ifc` | stdout | Count of every IFC type in the file | | `info` | `input.ifc` + type | stdout | Attributes of every element of that type | | `extract` | `input.ifc` | `output.ifc` | Extract types/presets into a new file | | `insert` | `base.ifc` + `part.ifc` | `output.ifc` | Merge part into base | | `replace` | `base.ifc` + space + `part.ifc` | `output.ifc` | Remove space from base, merge part in its place | | `split` | `input.ifc` | `outdir/*.ifc` | One file per storey, optional type filter | | `space` | `input.ifc` + space name | `output.ifc` | Extract a space and all its contained elements | | `move` | `input.ifc` + entity | `output.ifc` | Translate an element by (x, y, z) metres | | `copy` | `input.ifc` + ids/tags | `output.ifc` | Copy elements and translate by (x, y, z) metres | | `diff` | `source.ifc` + `target.ifc` | stdout or file | Show differences between two IFC files | | `remove` | `input.ifc` + type | `output.ifc` | Remove all elements of a given type | | `history` | `--input` / `--write-psets` | JSON or stamps | Write or read `Pset_GitCommit` on IFC elements | | `run` | - | - | Browse and execute yaml pipelines from demo repo | ## Presets `PRESETS` maps short names to lists of IFC types: | Preset | Expands to | |---------------|-----------------------------------------| | `walls` | `IfcWall`, `IfcWallStandardCase` | | `storey` | `IfcBuildingStorey` | | `furnitures` | `IfcFurnishingElement` | `resolve_types()` expands preset tokens before building ifcpatch queries. Raw `IfcType` tokens pass through unchanged, so presets and types can be mixed: ``` uv run ifccommit.py extract input.ifc out.ifc walls IfcSlab ``` ## ifcpatch recipes Two recipes are used: **`ExtractElements`** — filters elements by an ifcopenshell selector query and preserves the full spatial hierarchy (site → building → storey) and shared assets (materials, profiles, styles). The query syntax is: ``` IfcWall # all walls (includes subtypes) IfcWall, location = "Level 1" # walls contained in a named storey ``` **`MergeProjects`** — merges one or more `ifcopenshell.file` objects into a base model. Used by `insert`, `replace`, and `split` when multiple types are requested (since the `+` multi-type operator breaks the `location` filter — each type must be extracted separately then merged). ## Known quirks - **Passing file paths to `MergeProjects`** causes a segfault in the current ifcopenshell version. Always pass `ifcopenshell.file` objects. ([ifcopenshell.file](https://docs.ifcopenshell.org/autoapi/ifcopenshell/file/index.html)) - **`IfcType + IfcType, location = "X"`** — the `+` operator silently ignores the location filter. `split` works around this by extracting each type to a temp file then merging with `MergeProjects`. - **`IfcWall` covers subtypes** — querying `IfcWall` returns `IfcWallStandardCase` elements too (IFC inheritance), so the `walls` preset produces duplicates when both types are extracted with separate location queries and merged. ## Output convention Extracted files go in `dist/` (git-ignored). Naming: | Command | Output path | |----------------|---------------------------------------------------------| | `extract` | user-specified | | `insert` | user-specified | | `replace` | user-specified | | `split` | `/_.ifc` | | `space` | user-specified (or `_.ifc` for multi-match) | | `move` | user-specified | | `copy` | user-specified | | `diff` | stdout (or user-specified with `-o`) | | `remove` | user-specified (or `__removed.ifc`) | | `history` | JSON file (read mode) or stamps elements (write mode) | | `run` | (executes yaml pipeline in place) | ## Makefile targets | Target | Description | |-------------------|------------------------------------------------------| | `web` | Start the web app (production entry point) | | `web-dev` | Start uvicorn with auto-reload on port 8095 | | `kill` | Kill the running uvicorn process | | `install-service` | Install systemd service and nginx config | | `start` | `sudo systemctl start ifc-commit` | | `stop` | `sudo systemctl stop ifc-commit` | | `restart` | Rebuild and restart the service | | `status` | Tail the systemd journal | | `build` | Build static HTML (`build_html.py`) | | `release` | Sync source to release repo (`scripts/release.sh`) | | `clone-demo` | Clone the demo repo locally | | `reset-git` | Reset the demo repo to a clean state | | `clean` | Remove `dist/` |