pyvcad_attribute_resolver
Solver-based attribute resolution for OpenVCAD models. Automatically converts design-intent attributes (e.g. density, shore hardness) into machine-level attributes (e.g. nozzle temperature, flow-rate multiplier) using registered conversion data.
See the main OpenVCAD package for more info. For a deep-dive into the architecture, see design_doc.md.
Quick Start
import pyvcad as pv
import pyvcad_attribute_resolver as resolver
# Load built-in foaming filament data
resolver.register_foaming_conversions()
# Build a design with a physical attribute
sphere = pv.Sphere(pv.Vec3(0, 0, 0), 10.0)
sphere.set_attribute(pv.DefaultAttributes.TEMPERATURE,
pv.FloatAttribute("x + 210"))
# The resolver figures out: temperature -> flow_rate
result = resolver.adapt(sphere, ["flow_rate"], tags=["foaming_tpu"])
API Reference
| Function | Description |
|---|---|
register_conversion(source, target, converter_factory, ...) |
Register a new conversion edge |
unregister_conversion(name) |
Remove a conversion by name |
list_conversions() |
List all registered ConversionEntry objects |
clear_conversions() |
Remove all registered conversions |
adapt(design, targets, tags=..., ...) |
Wrap a design node with automatic conversions |
list_modules() |
Discover installed domain modules |
register_foaming_conversions() |
Register all 5 foaming filament conversions |
register_tpu_conversions() |
Register VarioShore TPU conversions only |
register_pla_conversions() |
Register LW-PLA conversions only |
register_j750_shore_hardness_conversions() |
Register J750 shore-hardness to volume-fractions conversions |
register_j750_modulus_toughness_conversions() |
Register the J750 modulus+toughness inverse-design conversion |
register_generic_color_conversions(alpha=1.0) |
Register machine-agnostic COLOR_RGB <-> COLOR_RGBA conversions |
register_vector_component_conversions(...) |
Register Vec3/Vec4 demote and/or promote conversions for caller-chosen names |
register_vec3_to_scalars_conversions(...) |
Register Vec3 -> X/Y/Z float demotion edges |
register_vec4_to_scalars_conversions(...) |
Register Vec4 -> X/Y/Z/W float demotion edges |
register_scalars_to_vec3_conversions(...) |
Register X/Y/Z floats -> Vec3 promotion |
register_scalars_to_vec4_conversions(...) |
Register X/Y/Z/W floats -> Vec4 promotion |
generate_j750_modulus_toughness_lookup_table(...) |
Regenerate the bundled J750 inverse lookup table |
generate_j750_modulus_toughness_reachable_region_plot(...) |
Regenerate the J750 reachable-region validation plot |
Adding a New Conversion Module
This section walks through adding your own material data to the resolver.
1. Create the Module Folder
Create a new sub-package under modules/:
resolver/src/pyvcad_attribute_resolver/modules/
└── your_material/
├── __init__.py
└── data.py # or .csv, .json — whatever suits your data
2. Add Your Data
Put your empirical measurements in data.py. Each dataset is a list of
(input_value, output_value) tuples:
# data.py
#: Density (g/cm³) -> temperature (°C)
DENSITY_TO_TEMPERATURE = [
(0.50, 260),
(0.65, 250),
(0.80, 240),
(1.00, 230),
]
#: Temperature (°C) -> flow rate multiplier
TEMPERATURE_TO_FLOW_RATE = [
(230, 1.20),
(240, 1.00),
(250, 0.85),
(260, 0.75),
]
Tip: If your data lives in CSV files, you can load them in
data.pyusingimportlib.resourcesorpathlib.Path(__file__).parentto locate the files relative to the module.
3. Write Registration Functions
In __init__.py, create factory functions and register them:
# __init__.py
import pyvcad as pv
from pyvcad_attribute_resolver.registry import register_conversion
from .data import DENSITY_TO_TEMPERATURE, TEMPERATURE_TO_FLOW_RATE
def _lut_factory(source_attr, target_attr, data):
"""Return a callable that builds a fresh LookupTableConverter."""
entries = [pv.LookupTableEntry(kv[0], kv[0], kv[1]) for kv in data]
def _factory():
return pv.LookupTableConverter(
[source_attr], [target_attr],
entries, pv.InterpolationMode.LINEAR,
)
return _factory
def register_your_material_conversions():
"""Register all conversions for your material."""
register_conversion(
source="density",
target="temperature",
converter_factory=_lut_factory(
pv.DefaultAttributes.DENSITY,
pv.DefaultAttributes.TEMPERATURE,
DENSITY_TO_TEMPERATURE,
),
name="your_material_density_to_temperature",
priority=0,
tags=["your_material"],
)
register_conversion(
source="temperature",
target="flow_rate",
converter_factory=_lut_factory(
pv.DefaultAttributes.TEMPERATURE,
pv.DefaultAttributes.FLOW_RATE,
TEMPERATURE_TO_FLOW_RATE,
),
name="your_material_temperature_to_flow_rate",
priority=0,
tags=["your_material"],
)
Key points:
namemust be globally unique across all modules.tagsshould identify your material so users can filter withtags=["your_material"]. Use distinct tags to avoid ambiguity with other modules that define the same conversion edges.required_inputsshould list every attribute the converter actually consumes. Leave it unset for ordinary one-input conversions; set it explicitly for cases likemodulus + toughness -> volume_fractions.converter_factorymust be a zero-arg callable that returns a new converter instance each time.
J750 Modulus/Toughness Module
The built-in j750_modulus_toughness module inverse-designs
volume_fractions from two design-intent attributes:
modulusin MPatoughnessin MJ/m^3
It uses the legacy C++ model to generate a 128 x 128 lookup table over:
log10(modulus [MPa]) in [-1.0, 3.5]toughness [MJ/m^3] in [0.0, 6.5]
The shipped runtime table assumes the J750 materials are:
Agilus30ClrVeroBlackM.Cleanser
and uses a default maximum cleanser volume fraction of 0.30.
To register the built-in table:
import pyvcad_attribute_resolver as resolver
resolver.register_j750_modulus_toughness_conversions()
To regenerate the lookup table with a different liquid cap or resolution:
resolver.generate_j750_modulus_toughness_lookup_table(
output_path="j750_custom_lut.json",
table_size=192,
max_liquid_volume=0.25,
fail_threshold=0.05,
)
Then register that table explicitly:
resolver.register_j750_modulus_toughness_conversions(
lookup_table_path="j750_custom_lut.json",
)
The generator stores a separate validity mask alongside the fraction grid. Invalid cells represent unreachable modulus/toughness targets; runtime lookup raises immediately instead of clamping to a nearby printable mixture.
4. Wire It Into the Package
Add a convenience re-export in the top-level __init__.py:
# resolver/src/pyvcad_attribute_resolver/__init__.py
from .modules.your_material import register_your_material_conversions
5. Use It
import pyvcad as pv
import pyvcad_attribute_resolver as resolver
resolver.register_your_material_conversions()
design = pv.Sphere(pv.Vec3(0, 0, 0), 10.0)
design.set_attribute(pv.DefaultAttributes.DENSITY,
pv.FloatAttribute(0.7))
# Automatically resolves: density -> temperature -> flow_rate
result = resolver.adapt(design, ["temperature", "flow_rate"],
tags=["your_material"])
6. Add Tests
Create a test file in resolver/tests/ (see test_foaming.py for an example):
# resolver/tests/test_your_material.py
import pytest
import pyvcad as pv
import pyvcad_attribute_resolver as resolver
@pytest.fixture(autouse=True)
def clean():
resolver.clear_conversions()
yield
resolver.clear_conversions()
def test_registration():
resolver.register_your_material_conversions()
names = {e.name for e in resolver.list_conversions()}
assert "your_material_density_to_temperature" in names
assert "your_material_temperature_to_flow_rate" in names
def test_adapt_chain():
resolver.register_your_material_conversions()
sphere = pv.Sphere(pv.Vec3(0, 0, 0), 1.0)
sphere.set_attribute("density", pv.FloatAttribute(0.7))
result = resolver.adapt(sphere,
["temperature", "flow_rate"],
tags=["your_material"])
assert "temperature" in result.attribute_list()
assert "flow_rate" in result.attribute_list()
Run with:
cd resolver && python -m pytest tests/ -v
Module Directory Structure
resolver/src/pyvcad_attribute_resolver/
├── __init__.py # core public API
├── adapt.py # adapt() entry point
├── conversion_entry.py # ConversionEntry dataclass
├── conversion_graph.py # directed graph
├── registry.py # global register/unregister/list/clear
├── solver.py # BFS pathfinder
└── modules/
├── __init__.py # list_modules() discovery
├── foaming/ # built-in foaming filament data
│ ├── __init__.py
│ └── data.py
└── your_model(s)/ # your new module
├── __init__.py
└── data.py
Release files for pyvcad-attribute-resolver 3.0.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pyvcad_attribute_resolver-3.0.2-py3-none-any.whl | Python 3 | none | any | Details |
Release files / pyvcad_attribute_resolver-3.0.2-py3-none-any.whl
| Download URL | pyvcad_attribute_resolver-3.0.2-py3-none-any.whl |
|---|---|
| Size | 811.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
1c896392124429e2f290a3677cfc0cd173fb4ca3f6aa9bd5e1fb444acc94bf53
|
|
BLAKE2b-256 checksum How to use checksums |
32b966c19b68217283d51a84cbdda558898e8548e7cfcf4ab1b49520ed2f961b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.2
|