Files
POPS/app/estimation.py
Martin Hohenberg 908cfbc981 Fix estimation: replace 20% infill with 50% material factor, 32 g/h speed
The 20% infill assumption ignored walls and solid layers which dominate
most prints. Validated against Bambu Lab Studio for a GF lid:
  model volume 26.09 cm³ × 0.50 × 1.24 g/cm³ = 16.2 g  (actual: 16.24 g)
  16.2 g / 32 g/h × 60 = 30 min                          (actual: 30.5 min)

New env vars (read at request time, no rebuild needed):
  MATERIAL_FACTOR=0.50        fraction of model volume that becomes filament
  PRINT_SPEED_G_PER_HOUR=32   matches Bambu Lab at normal quality

Card header now shows active assumptions including speed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 22:48:56 +02:00

59 lines
2.1 KiB
Python

"""Job cost and time estimation from STL geometry."""
import os
# g/cm³ per material
_DENSITY: dict[str, float] = {
"PLA": 1.24,
"PETG": 1.27,
"ABS": 1.04,
"ASA": 1.07,
"TPU": 1.20,
"Nylon": 1.14,
"PC": 1.20,
}
_DEFAULT_DENSITY = 1.24
def estimate(volume_ccm: float, material: str | None) -> dict:
"""
Returns a cost/time estimate dict for one job.
MATERIAL_FACTOR (env, default 0.50):
Fraction of model volume that becomes actual filament.
'20 % infill' in the slicer does NOT mean 20 % of volume is printed —
walls, top/bottom solid layers, and shells are 100 % solid and typically
dominate. Empirically ~50 % of model volume is printed for typical FDM
parts (validated against Bambu Lab Studio output).
PRINT_SPEED_G_PER_HOUR (env, default 32):
Average material throughput in g/h. ~32 g/h matches Bambu Lab printers
at normal quality; slower printers (~20 g/h) can be set here.
"""
vat_rate = float(os.environ.get("VAT_RATE", 19))
price_per_kg = float(os.environ.get("FILAMENT_PRICE_PER_KG", 15))
material_factor = float(os.environ.get("MATERIAL_FACTOR", 0.50))
print_speed = float(os.environ.get("PRINT_SPEED_G_PER_HOUR", 32))
density = _DENSITY.get(material or "", _DEFAULT_DENSITY)
material_ccm = volume_ccm * material_factor
weight_g = material_ccm * density
print_minutes = round((weight_g / print_speed) * 60)
filament_cost = weight_g / 1000 * price_per_kg
after_misprint = filament_cost * 1.20
net_price = after_misprint * 1.30
gross_price = net_price * (1 + vat_rate / 100)
return {
"material_ccm": round(material_ccm, 2),
"weight_g": round(weight_g, 1),
"print_minutes": print_minutes,
"filament_cost": round(filament_cost, 2),
"net_price": round(net_price, 2),
"gross_price": round(gross_price, 2),
"vat_rate": vat_rate,
"price_per_kg": price_per_kg,
"material_factor": material_factor,
"print_speed": print_speed,
}