Skip to main content

fdm-price-calc

A CLI tool and Python library for calculating FDM 3D print job costs. Implements the same pricing model as a standard product pricing worksheet: filament cost × efficiency factor + machine time cost + labor = total landed cost, with suggested sell prices at 50 / 60 / 70 % margin.

Installation

pip install fdm-price-calc

Using uv:

# install as a CLI tool
uv tool install fdm-price-calc

# or as a library inside a project
uv add fdm-price-calc

CLI

fdmpricecalc [FILE] [PRINTER] [FILAMENT] [options]
fdmpricecalc new-printer | new-filament | list

FILE is a Supported sliced file . PRINTER and FILAMENT are the names of saved presets (see Presets). All three positional arguments are optional — Missing values are filled in interactively.

Preset auto-detection from file

If the file parcer suppports it, the CLI reads the embedded slicer metadata and tries to match your saved presets automatically before prompting:

  • Printer — reads the printer_model field (e.g., "Bambu Lab P1S") and scores saved presets by word overlap. The best match is suggested, and you can accept or pick a different one.
  • Filament — matches each slot's material type (e.g., PLA) against saved filament presets. Works for both single-filament and multi-filament jobs.

With -y, the match is used silently; if nothing matches, the run errors with a helpful message instead of hanging on a prompt.

# Full run from a sliced file with saved presets
fdmpricecalc job.3mf my_printer pla_basic -n "Widget v2" -a Alice

# Omit PRINTER and FILAMENT — auto-detected from the file
fdmpricecalc job.3mf -n "Widget v2" -a Alice

# Fully scripted with auto-detection — no prompts at all
fdmpricecalc job.3mf -n "Widget" -a Alice -l 5 -q 2 -y

# No file — enter print stats manually via prompts
fdmpricecalc --no-file my_printer pla_basic

# No file — fully scripted, zero prompts
fdmpricecalc --no-file my_printer pla_basic -w 224.93 -t "5h6m12s" \
  --material PLA --color "#FF8800" -n "Pages" -a me -l 10 -q 2 -y

# Override a preset value for this run only
fdmpricecalc job.3mf my_printer pla_basic --efficiency 1.2 --filament-cost 35

# Save result to JSON — minimal by default (no plate data, presets by name)
fdmpricecalc job.3mf bambu_p1s bambu_pla_basic -n "Widget" -o result.json

# Include raw per-plate slicer data
fdmpricecalc job.3mf bambu_p1s bambu_pla_basic -n "Widget" -o result.json -f

# Embed full preset data so the file is self-contained
fdmpricecalc job.3mf bambu_p1s bambu_pla_basic -n "Widget" -o result.json -s

If an unsupported file type is given, the tool falls back to manual entry automatically.

The default JSON output is minimal — just the inputs you provided (printer, filament, labor, quantity, extra costs) and the calculated costs. The raw per-plate slicer data is omitted since it can always be re-read from the source file. Add -f to include it, or -s to embed full preset data for archiving.

Multi-filament

For .3mf files with multiple filament slots, the CLI will automatically try to match each slot to a saved preset by material type (e.g., a slot reporting PLA will match any preset whose material field is PLA). You can also map slots manually:

# Map slot 1 to orange PLA, slot 2 to white PLA; all other slots fall back to pla_basic
fdmpricecalc job.3mf my_printer pla_basic \
  --filament-map 1:pla_orange \
  --filament-map 2:pla_white

# Override cost per kg for all PLA slots in this run only (doesn't change saved presets)
fdmpricecalc job.3mf my_printer pla_basic --material-cost PLA:18

# Also override the efficiency/markup factor for this material
fdmpricecalc job.3mf my_printer pla_basic --material-cost PLA:18:1.8

All flags

  -y, --non-interactive   Never prompt — use provided args and defaults.
                          If PRINTER or FILAMENT are omitted, attempts to
                          auto-detect from file metadata before erroring.
                          Defaults: labor=0, qty=1, no extra costs.
                          Multi-filament slots are auto-matched to saved presets.

job metadata:
  -n, --name NAME       Job name
  -a, --author AUTHOR   Job author
  -o, --output FILE     Save results to JSON file
  -f, --full            Include raw per-plate slicer data in the JSON
                        (plates + totals; omitted by default as they can be
                        re-read from the source file)
  -s, --self-contained  Embed full preset data in the JSON instead of
                        referencing by name (useful for archiving jobs)
  -l, --labor MIN       Post-processing labor time in minutes
  --labor-rate $/HR     Labor hourly rate (overrides preset value for this run)
  -q, --qty N           Quantity to produce
  --extra NAME:COST[:QTY]
                        Extra per-unit cost (hardware, inserts, etc.).
                        Repeatable: --extra 'Magnet:0.50:2' --extra 'Insert:0.08'

manual print stats (use with --no-file or as file fallback):
  -m, --no-file         Skip file parsing; enter stats via args or prompts
  -w, --weight G        Total filament weight in grams
  -t, --time DURATION   Total print time (90, 1h30m, 1:30, 5h6m12s, …)
  --material TYPE       Filament material (PLA, PETG, …)
  --color HEX           Filament color hex (e.g. #FF8800)

filament preset overrides:
  --filament-cost $/KG  Override filament cost per kg (default/single preset)
  --filament-map ID:PRESET
                        Map a filament slot ID to a preset (repeatable)
  --material-cost MATERIAL:$/KG[:MARKUP]
                        Override cost per kg (and optionally the efficiency/markup
                        factor) for all slots of a given material type
                        (e.g., PLA:18 or PLA:18:1.8). Repeatable.
  --efficiency X        Material efficiency factor (default 1.5)

printer preset overrides:
  --print-rate $/HR     Override machine cost rate ($/hr)
  --printer-cost $      Printer purchase cost
  --upfront $           Additional upfront cost
  --maintenance $/YR    Estimated annual maintenance
  --life YRS            Estimated printer life in years
  --uptime PCT          Estimated uptime 0–100
  --power W             Printer power consumption in watts
  --electricity $/KWH   Electricity cost per kWh
  --buffer X            Printer cost buffer factor

Other commands

fdmpricecalc new-printer           # interactive printer, preset wizard
fdmpricecalc new-filament          # interactive filament preset wizard
fdmpricecalc list                  # list all saved presets
fdmpricecalc list printers
fdmpricecalc list filaments

Presets

The package ships with built-in presets for common Bambu and Prusa printers and filaments. They are available immediately after installation with no setup required.

Bundled printers: bambu_a1_mini, bambu_a1, bambu_p1p, bambu_p1s, bambu_p2s, bambu_x1c, bambu_x1e, bambu_h2d, bambu_h2s, bambu_h2c, prusa_mini_plus, prusa_mk4s, prusa_xl, prusa_core_one

Bundled filaments: bambu_pla_basic, bambu_petg_basic, bambu_abs, bambu_tpu, prusament_pla, prusament_petg, prusament_asa

User presets are stored as plain JSON files and always take priority over bundled presets with the same name:

~/.local/share/fdmpricecalc/
    printers/    ← one .json per printer preset
    filaments/   ← one .json per filament preset

To customize a bundled preset, save it under the same name and your version will be used from then on:

fdmpricecalc new-printer   # wizard saves to your user dir
# or edit the JSON directly at the path shown by fdmpricecalc --help

Run fdmpricecalc list to see all available presets (bundled + user). Run fdmpricecalc --help to see the exact user preset paths on your system.

Printer preset fields (printers/my_printer.json):

Field Default Description
name — Display name
printer_cost 800 Purchase cost ($)
additional_upfront_cost 0 Upgrades at purchase ($)
annual_maintenance 80 Estimated yearly upkeep ($)
estimated_life_years 5 Expected printer lifespan
estimated_uptime_pct 0.5 Fraction of hours the printer runs
power_watts 160 Average power draw (W)
electricity_cost_per_kwh 0.14 Electricity rate ($/kWh)
printer_cost_buffer_factor 1.3 Multiplier for unexpected costs
material_efficiency_factor 1.5 Filament waste multiplier
labor_hourly_rate 15 Post-processing labor rate ($/hr)
print_time_rate_override null Skip auto-calc and use a fixed $/hr

Filament preset fields (filaments/my_filament.json):

Field Default Description
name — Display name
material PLA Material type string
cost_per_kg 20 Cost per kilogram ($)

Pricing model

filament_cost  = (cost_per_kg / 1000) × weight_g × efficiency_factor × qty
machine_cost   = print_time_rate × print_time_hr × qty
labor_cost    = (labor_min / 60) × labor_hourly_rate × qty
extra_cost     = Σ (unit_cost × item_qty) × qty   ← hardware, inserts, etc.

total_landed   = filament_cost + machine_cost + labor_cost + extra_cost

sell_price     = total_landed / (1 − margin)
                 e.g. 50% margin → total_landed × 2
                      60% margin → total_landed × 2.5
                      70% margin → total_landed × 3.33

print_time_rate (auto) = (capital_cost_per_hr + electrical_cost_per_hr)
                         × buffer_factor

Supported file formats

Format Notes
.gcode.3mf Bambu Studio sliced projects; reads slice_info.config for print data and project_settings.config for printer model and filament types
.json Json file produced by the --output option
.gcode PrusaSlicer text G-code; reads embedded config comments
.bgcode PrusaSlicer binary G-code; reads PRINTER_METADATA and PRINT_METADATA blocks

Python library

All pricing inputs are set directly on the JobData object before calling calculate(). Parsers populate the file data (plates, weight, print time); Your code attaches the pricing config.

import fdm_price_calc as fdm

# Parse a sliced .3mf
job = fdm.parse("job.3mf")

# Or build job data manually
job = fdm.manual_job(
    weight_g=224.93,
    print_time_s=fdm.parse_duration("5h6m12s"),
    material="PLA",
)

# Load saved presets (or create inline)
printer  = fdm.PrinterPreset.load("bambu_p1s")
filament = fdm.FilamentPreset.load("bambu_pla_basic")

# Attach pricing config to the job
job.printer       = printer
job.filament      = filament
job.labor_minutes = 10
job.quantity      = 3
job.extra_costs   = [
    fdm.ExtraCost(name="Magnet",    unit_cost=0.50, qty=2),
    fdm.ExtraCost(name="M3 insert", unit_cost=0.08, qty=4),
]

result = fdm.calculate(job)

print(f"Landed cost:  ${result.total_landed_cost:.2f}")
print(f"50% margin:   ${result.price_at_margin['50%']:.2f}")
print(f"60% margin:   ${result.price_at_margin['60%']:.2f}")

# Save presets for later
printer.save()
filament.save()

Multi-filament (per-slot pricing)

For .3mf files where each slot may use a different filament:

job = fdm.parse("multi_color.gcode.3mf")

orange = fdm.FilamentPreset(name="Orange PLA", cost_per_kg=22)
white  = fdm.FilamentPreset(name="White PLA",  cost_per_kg=25)
pla    = fdm.FilamentPreset.load("pla_basic")

job.printer          = printer
job.filament         = {1: orange, 2: white}  # dict[slot_id, preset]
job.default_filament = pla                     # fallback for unmapped slots
job.labor_minutes    = 5
job.quantity         = 1

result = fdm.calculate(job)
# result.filament_breakdown — list of (label, cost) per slot

Public API

Symbol Type Description
parse(path) function Parse a supported file → JobData
manual_job(weight_g, print_time_s, ...) function Build JobData without a file
calculate(job) function Returns CostBreakdown; all inputs read from job
parse_duration(s) function "1h30m" → seconds (int)
JobData dataclass Job data + pricing config (see fields below)
PlateData dataclass Per-plate print time, weight, filaments
FilamentUsage dataclass Per-filament usage within a plate
ExtraCost dataclass Extra per-unit cost item (name, unit_cost, qty)
PrinterPreset dataclass Printer config with cost calculations
FilamentPreset dataclass Filament name, material, $/kg
CostBreakdown dataclass Cost components + margin prices
BaseParser ABC Base class for adding new file formats
list_printers() function Names of saved printer presets
list_filaments() function Names of saved filament presets
data_dir() function Root preset storage path
printers_dir() function Printer preset directory
filaments_dir() function Filament preset directory

JobData pricing fields (set by caller, not by parsers):

Field Type Default Description
printer_hint str | None None Printer model string from file metadata; used by CLI for auto-matching (read-only for library users)
printer PrinterPreset | None None Required before calculate()
filament FilamentPreset | dict[int, FilamentPreset] | None None Single preset or per-slot mapping
default_filament FilamentPreset | None None Fallback for unmapped slots (dict mode)
labor_minutes float 0.0 Post-processing labor time
quantity int 1 Number of copies
extra_costs list[ExtraCost] [] Extra per-unit cost items (hardware, inserts, etc.)
name str "" Job display name
author str "" Job author

Metadata

Release files for fdm-price-calc 1.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for fdm-price-calc 1.0.0
File Size Uploaded
fdm_price_calc-1.0.0.tar.gz 26.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for fdm-price-calc 1.0.0
File Interpreter ABI Platform
fdm_price_calc-1.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 66.2 kB

Release files / fdm_price_calc-1.0.0.tar.gz

Download URL fdm_price_calc-1.0.0.tar.gz
Size 26.8 kB
Tags Source
SHA-256 checksum
How to use checksums
914dac9eabb591da2f2a2e16d83eb7610d4579d4a66631cdcaab2c650f507bb1
BLAKE2b-256 checksum
How to use checksums
fb2c988ec158ec2b6cbe18ba550322686013d604282b67739e4ce0c847dc8eb0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on May 17, 2026.

Transparency log

Release files / fdm_price_calc-1.0.0-py3-none-any.whl

Download URL fdm_price_calc-1.0.0-py3-none-any.whl
Size 39.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
127d3596accca6a652b5d4070bbe837b8b9e6221d17576c535e2416407f7f968
BLAKE2b-256 checksum
How to use checksums
de8895bad9bc880af8244c1af39b7b776d489337e9808ff89c076d133ccc969d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on May 17, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page