Skip to main content

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.py using importlib.resources or pathlib.Path(__file__).parent to 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:

  • name must be globally unique across all modules.
  • tags should identify your material so users can filter with tags=["your_material"]. Use distinct tags to avoid ambiguity with other modules that define the same conversion edges.
  • required_inputs should list every attribute the converter actually consumes. Leave it unset for ordinary one-input conversions; set it explicitly for cases like modulus + toughness -> volume_fractions.
  • converter_factory must 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:

  • modulus in MPa
  • toughness in 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:

  • Agilus30Clr
  • VeroBlack
  • M.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.1.0

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

Built distribution (wheel)

Table of built distributions (wheels) for pyvcad-attribute-resolver 3.1.0
File Interpreter ABI Platform
pyvcad_attribute_resolver-3.1.0-py3-none-any.whl Python 3 none any Details

Release files / pyvcad_attribute_resolver-3.1.0-py3-none-any.whl

Download URL pyvcad_attribute_resolver-3.1.0-py3-none-any.whl
Size 811.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ccf37aaa0a801b23db09e6ea4d82d6286316b2c8fcda5d050fdc7ae437302e06
BLAKE2b-256 checksum
How to use checksums
f9e25ee7e45d34924f27f9c5ca50ff5ad136fb0ed5b8947e495050105ad7aa9a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.2

Release history Release notifications | RSS feed

This release

3.1.0 This release

1 release file

3.0.2

1 release file

3.0.1

1 release file

3.0.0

1 release file

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