ifc-commit/docs/architecture.md
2026-03-24 16:11:15 +01:00

124 lines
6.9 KiB
Markdown

# 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` | `<outdir>/<stem>_<StoreyName>.ifc` |
| `space` | user-specified (or `<stem>_<Name>.ifc` for multi-match) |
| `move` | user-specified |
| `copy` | user-specified |
| `diff` | stdout (or user-specified with `-o`) |
| `remove` | user-specified (or `<stem>_<type>_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/` |