Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

qto — open quantity takeoff with pluggable classification

Point it at an IFC model, name a cost-code standard, get a CSV of quantities per element mapped to that standard.

qto model.ifc --classification uniformat
global_id,entity,predefined_type,name,type_name,storey,material,standard,code,code_title,quantity,unit,quantity_name,status,embedded_code
000000000000000000000D,IFCWALL,SOLIDWALL,W-01 Tường trục A,Tường bao 220,Tầng 1,Gạch đất sét nung,uniformat,B2011,Exterior Wall Construction,22.8,m2,NetSideArea,ok,B2010

Each row records which quantity was believed (NetSideArea), so a number you disagree with is traceable back to the model in one step.

The engine is generic. The mappings are the contribution. UniFormat, MasterFormat, NRM, OmniClass and Vietnamese định mức are all just TOML files in qto/mappings/ — no Python involved in adding a standard, a national cost-norm system, or your firm's in-house code list.

One dependency — ifc-spf, the standard-library IFC reader this project was extracted from. Python 3.11+. No geometry kernel, no IfcOpenShell, no BIM server.


Why this exists

Every estimating tool re-implements the same two halves: read the model, then map elements to whatever cost breakdown your client's contract uses. The first half is the same everywhere. The second half is where all the local knowledge lives — and it is the half that is always locked inside a commercial tool.

So: the first half is a library, the second half is a folder of data files anyone can send a PR against. A quantity surveyor who has never written Python can fix a code, add a work section, or contribute a whole national standard by editing TOML.

Install

pip install qto-takeoff

or from a checkout:

pip install -e .

Use

qto model.ifc -c uniformat                       # one row per element
qto model.ifc -c nrm --group-by code             # elemental bill of quantities
qto model.ifc -c nrm --group-by code,storey      # ...broken down by level
qto model.ifc -c dinh-muc -f md                  # markdown for a report
qto model.ifc -c ./our-house-standard.toml       # your own mapping
qto model.ifc -c uniformat -o takeoff.csv        # write a file

qto inspect model.ifc                            # what is actually in there
qto standards                                    # what mappings ship
qto validate qto/mappings/nrm.toml               # lint a mapping

Grouped by code, the sample model in tests/fixtures/ gives:

| code  | code_title                       | quantity | unit | elements |
|:------|:---------------------------------|---------:|:-----|---------:|
| A1012 | Column Foundations and Pile Caps |    1.152 | m3   |        2 |
| A1031 | Standard Slab on Grade           |       48 | m2   |        1 |
| B1011 | Floor Structural Frame           |    2.209 | m3   |        5 |
| B1012 | Floor Decks, Slabs and Toppings  |     46.5 | m2   |        1 |
| B1022 | Roof Decks and Sheathing         |       52 | m2   |        1 |
| B2011 | Exterior Wall Construction       |    95.22 | m2   |        5 |
| B2021 | Exterior Windows                 |        3 | nr   |        3 |
| B2031 | Exterior Doors                   |        2 | nr   |        2 |
| B3011 | Roof Coverings                   |       52 | m2   |        1 |
| C1011 | Fixed Partitions                 |    20.82 | m2   |        2 |
| C1021 | Interior Doors                   |        1 | nr   |        1 |
| C2011 | Stair Construction               |        1 | nr   |        1 |
| C2013 | Stair Railings and Balustrades   |      4.5 | m    |        1 |
| C3021 | Floor Finishes                   |     46.5 | m2   |        1 |
| C3031 | Ceiling Finishes                 |     46.5 | m2   |        1 |
| D2020 | Domestic Water Distribution      |       20 | m    |        2 |

Every run prints a coverage summary to stderr, because what it missed is the number you actually need before you trust a takeoff:

model      model.ifc (IFC4, 303 instances)
standard   uniformat — UNIFORMAT II elemental classification (ASTM E1557) [draft]
elements   31 taken off, 1 parts suppressed
classified 30 (96.8%), 0 without a usable quantity, 1 unclassified
codes      16 of 20 in the mapping
gaps       IFCBUILDINGELEMENTPROXY x1

The standards that ship

Standard What it is Rules Coverage on the sample model
uniformat UNIFORMAT II elemental (ASTM E1557) 21 96.8%
masterformat CSI MasterFormat work results 21 96.8%
nrm RICS NRM1 elemental cost planning 20 96.8%
omniclass OmniClass Table 21 (Elements) 21 96.8%
dinh-muc Định mức dự toán XD (TT 12/2021/TT-BXD) 8 48.4%

All five are marked status = "draft". They are a working skeleton written from the published structure of each standard, not a certified transcription — qto standards prints each mapping's declared gaps, and the định mức mapping is deliberately shipped half-finished because the people who can finish it are the ones who do dự toán for a living, not the ones who wrote the parser.

The same model, two standards, on purpose:

uniformat  B2011 Exterior Wall Construction        95.22 m2
dinh-muc   AE.222 Xây tường gạch, dày ≤ 33 cm      18.31 m3

Vietnamese cost norms measure masonry by volume where UNIFORMAT measures walls by area. One model has to answer both questions without being re-authored — that constraint is why the engine is generic.

How it reads a model

  • The IFC reader is ifc-spf — a standard-library STEP parser plus name-addressed schema tables and unit resolution, extracted from this project so a building-code checker could use the same reader. IFC is a text format; a takeoff only needs to walk the instance graph.
  • Quantities come from IfcElementQuantity — the Qto_*BaseQuantities your authoring tool exports. Nothing is derived from geometry, so a number in the output is a number someone's BIM tool actually wrote. If the model carries no quantities, qto inspect says so instead of inventing them.
  • Units are normalised through SI. Real exports mix them — millimetre lengths beside square-metre areas is routine — and an SI prefix on an area unit is squared, not applied once. That is the 10⁶ error that ruins takeoffs, so it has its own module and its own tests.
  • Properties are inherited from the type. Pset_WallCommon.IsExternal usually sits on IfcWallType, not on the wall; occurrence values override.
  • Materials resolve through layer sets, and the thickest layer wins as the primary material — a 220 mm wall is brick, not the 15 mm render on it.
  • Assemblies never double count. For a stair and its flights, whichever side carries the quantities is the side taken off. --include-parts opts out; the coverage line tells you how many were suppressed.

Speed is unremarkable and honest about it: a 6.8 MB model with 91k instances takes about 1.7 s end to end, scaling roughly linearly. The whole file is parsed into memory, so a 200 MB federated model wants a machine with room for it.

Writing a mapping

A rule says which elements it claims and which quantity to take:

[[rule]]
code = "B2011"
title = "Exterior Wall Construction"
unit = "m2"
entity = ["IfcWall"]                                    # subtypes included
property = { "Pset_WallCommon.IsExternal" = true }
quantity = ["NetSideArea", "GrossSideArea"]             # in preference order

Elements are claimed by what the model says they are, not by what someone typed in a name field. Rules can also match predefined_type, material, type_name, name, storey, and any property with regex / in / min / max / exists tests, plus an exclude block. The most specific rule wins; priority overrides that when you need it to.

Full reference: docs/mapping-format.md.

What this does not do

Stated plainly, because a takeoff tool that overstates itself is dangerous:

  • It does not measure geometry. No areas from BREPs, no wall girths, no deductions for openings beyond what the exporter already netted off. If the model has no Qto_* quantities, there is nothing to take off.
  • It does not apply measurement rules. NRM2's "no deduction under 1.00 m²", SMM7 conventions, VN quy tắc đo bóc — none of that is implemented. Output is model quantities mapped to codes, not a compliant BoQ.
  • It does not price anything. Codes out; rates are your estimating system's job.
  • The shipped code lists are drafts and need review by people who use each standard professionally.

Contributing

The engine needs very little. The mappings need a lot.

Adding or fixing a standard is one TOML file plus one golden CSV, and CI checks it lints, has no dead rules, and produces the bill of quantities you reviewed. See CONTRIBUTING.md.

Especially wanted: NRM2 detailed measurement, DIN 276, Uniclass 2015, Vietnamese định mức chapters AB/AI and cửa/vách codes, and any national standard whose knowledge currently lives only inside paid software. Issue #1 lists what is open — claim one by commenting.

make test       # run the suite
make validate   # lint every mapping
make golden     # refresh the expected takeoffs after a mapping change

Using it as a library

The reader is meant to be shared. If you are writing another IFC tool, import the layers you need instead of writing a fourth STEP parser:

from qto.spf import load            # STEP -> entities, refs, typed values
from qto.model import Model         # indexing, deref, inverse refs, units
from qto.extract import extract     # elements with quantities in SI
from qto import rules, report       # classification and output

model = Model.open("model.ifc")
model.scale.length                  # metres per model length unit
elements = extract(model)           # normalised, unit-converted, no geometry

qto.spf and qto.units are deliberately domain-free — qto's own cost mappings, ecfactors (embodied carbon) and plancheck (building-code rules) all sit on top of them.

Licence

MIT.

About

Open quantity takeoff from IFC with pluggable cost classification — UniFormat, MasterFormat, NRM, OmniClass, định mức. The engine is generic; the mappings are the contribution.

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages