pyvcad-attribute-resolver 3.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyvcad_attribute_resolver/__init__.py +25 -0
- pyvcad_attribute_resolver/adapt.py +52 -0
- pyvcad_attribute_resolver/conversion_entry.py +35 -0
- pyvcad_attribute_resolver/conversion_graph.py +58 -0
- pyvcad_attribute_resolver/modules/__init__.py +14 -0
- pyvcad_attribute_resolver/modules/foaming/.gitignore +1 -0
- pyvcad_attribute_resolver/modules/foaming/__init__.py +110 -0
- pyvcad_attribute_resolver/modules/foaming/data.py +256 -0
- pyvcad_attribute_resolver/modules/foaming/plot_data.py +397 -0
- pyvcad_attribute_resolver/modules/generic_color/__init__.py +69 -0
- pyvcad_attribute_resolver/modules/generic_color/data.py +6 -0
- pyvcad_attribute_resolver/modules/j750_modulus_toughness/__init__.py +153 -0
- pyvcad_attribute_resolver/modules/j750_modulus_toughness/__marimo__/session/j750_modulus_toughness_companion.py.json +269 -0
- pyvcad_attribute_resolver/modules/j750_modulus_toughness/data.py +646 -0
- pyvcad_attribute_resolver/modules/j750_modulus_toughness/j750_modulus_toughness_companion.py +677 -0
- pyvcad_attribute_resolver/modules/j750_modulus_toughness/j750_modulus_toughness_lut.json +99252 -0
- pyvcad_attribute_resolver/modules/j750_modulus_toughness/legacy_characterization.json +2908 -0
- pyvcad_attribute_resolver/modules/j750_modulus_toughness/notebook_support.py +1567 -0
- pyvcad_attribute_resolver/modules/j750_shore_hardness/__init__.py +111 -0
- pyvcad_attribute_resolver/modules/j750_shore_hardness/data.py +53 -0
- pyvcad_attribute_resolver/modules/j750_shore_hardness/fit_curve.py +344 -0
- pyvcad_attribute_resolver/modules/j750_shore_hardness/fitted_model.py +167 -0
- pyvcad_attribute_resolver/modules/j750_shore_hardness/j750_shore_hardness_data.csv +21 -0
- pyvcad_attribute_resolver/modules/stratasys_radiomatrix/__init__.py +182 -0
- pyvcad_attribute_resolver/modules/stratasys_radiomatrix/data.py +170 -0
- pyvcad_attribute_resolver/registry.py +48 -0
- pyvcad_attribute_resolver/solver.py +126 -0
- pyvcad_attribute_resolver-3.0.0.dist-info/METADATA +497 -0
- pyvcad_attribute_resolver-3.0.0.dist-info/RECORD +31 -0
- pyvcad_attribute_resolver-3.0.0.dist-info/WHEEL +4 -0
- pyvcad_attribute_resolver-3.0.0.dist-info/licenses/LICENSE +185 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from .registry import (register_conversion, unregister_conversion,
|
|
2
|
+
list_conversions, clear_conversions)
|
|
3
|
+
from .adapt import adapt
|
|
4
|
+
from .solver import ResolverError, NoPathError, AmbiguousPathError
|
|
5
|
+
from .conversion_entry import ConversionEntry
|
|
6
|
+
from .modules.foaming import (register_foaming_conversions,
|
|
7
|
+
register_tpu_conversions,
|
|
8
|
+
register_pla_conversions)
|
|
9
|
+
from .modules.j750_shore_hardness import (
|
|
10
|
+
fit_j750_shore_hardness_curve,
|
|
11
|
+
generate_j750_shore_hardness_fit_plot,
|
|
12
|
+
register_j750_shore_hardness_conversions,
|
|
13
|
+
)
|
|
14
|
+
from .modules.j750_modulus_toughness import (
|
|
15
|
+
register_j750_modulus_toughness_conversions,
|
|
16
|
+
generate_j750_modulus_toughness_lookup_table,
|
|
17
|
+
generate_j750_modulus_toughness_reachable_region_plot,
|
|
18
|
+
)
|
|
19
|
+
from .modules.stratasys_radiomatrix import (
|
|
20
|
+
register_stratasys_radiomatrix_conversions,
|
|
21
|
+
)
|
|
22
|
+
from .modules.generic_color import (register_generic_color_conversions,
|
|
23
|
+
register_rgb_to_rgba_conversion,
|
|
24
|
+
register_rgba_to_rgb_conversion)
|
|
25
|
+
from .modules import list_modules
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import pyvcad as pv
|
|
2
|
+
from .registry import _global_graph
|
|
3
|
+
from .solver import solve
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def adapt(design, target_attributes,
|
|
7
|
+
allowed_conversions=None, tags=None, max_depth=10):
|
|
8
|
+
"""Wrap a design node with AttributeModifiers to produce target attributes.
|
|
9
|
+
|
|
10
|
+
For each target attribute not already present on the design, the
|
|
11
|
+
solver finds a conversion path through the registered graph and
|
|
12
|
+
wraps the design in the corresponding ``AttributeModifier`` chain.
|
|
13
|
+
|
|
14
|
+
Targets are processed sequentially so that attributes produced by
|
|
15
|
+
earlier conversions are available for later ones.
|
|
16
|
+
|
|
17
|
+
Arguments:
|
|
18
|
+
design: ``pyvcad.Node``, the root of the design tree.
|
|
19
|
+
target_attributes: List of attribute name strings the caller
|
|
20
|
+
needs on the output.
|
|
21
|
+
allowed_conversions: Optional list of ``ConversionEntry`` names
|
|
22
|
+
to restrict the solver.
|
|
23
|
+
tags: Optional list of tags to filter conversions.
|
|
24
|
+
max_depth: Maximum chain length per target (default 10).
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
``pyvcad.Node`` — the original design wrapped in zero or more
|
|
28
|
+
``AttributeModifier`` nodes. Returns the design unchanged if
|
|
29
|
+
all targets are already present.
|
|
30
|
+
|
|
31
|
+
Raises:
|
|
32
|
+
NoPathError: If any target attribute cannot be reached.
|
|
33
|
+
AmbiguousPathError: If disambiguation fails for any target.
|
|
34
|
+
"""
|
|
35
|
+
available = design.attribute_list()
|
|
36
|
+
result = design
|
|
37
|
+
|
|
38
|
+
for target in target_attributes:
|
|
39
|
+
if target in available:
|
|
40
|
+
continue
|
|
41
|
+
|
|
42
|
+
path = solve(_global_graph, available, target,
|
|
43
|
+
allowed_conversions=allowed_conversions,
|
|
44
|
+
tags=tags, max_depth=max_depth)
|
|
45
|
+
|
|
46
|
+
for entry in path:
|
|
47
|
+
converter = entry.converter_factory()
|
|
48
|
+
result = pv.AttributeModifier(converter, result)
|
|
49
|
+
|
|
50
|
+
available = result.attribute_list()
|
|
51
|
+
|
|
52
|
+
return result
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
class ConversionEntry:
|
|
2
|
+
"""A registered conversion between two attribute types.
|
|
3
|
+
|
|
4
|
+
Each entry represents a directed edge in the conversion graph, from a
|
|
5
|
+
source attribute to a target attribute. The ``converter_factory`` callable
|
|
6
|
+
produces a fresh ``AttributeConverterBase`` instance each time it is
|
|
7
|
+
invoked, supporting clone-safe, thread-safe parallel evaluation.
|
|
8
|
+
|
|
9
|
+
Attributes:
|
|
10
|
+
source: Input attribute name.
|
|
11
|
+
target: Output attribute name.
|
|
12
|
+
converter_factory: Zero-argument callable returning an
|
|
13
|
+
``AttributeConverterBase``.
|
|
14
|
+
name: Human-readable identifier for this conversion.
|
|
15
|
+
priority: Higher values are preferred when disambiguating
|
|
16
|
+
multiple equally-short paths.
|
|
17
|
+
tags: Context-filtering labels (e.g. ``"foaming_tpu"``).
|
|
18
|
+
required_inputs: Attribute names that must already be present on the
|
|
19
|
+
design before this conversion can be applied.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(self, source, target, converter_factory,
|
|
23
|
+
name=None, priority=0, tags=None, required_inputs=None):
|
|
24
|
+
self.source = source
|
|
25
|
+
self.target = target
|
|
26
|
+
self.converter_factory = converter_factory
|
|
27
|
+
self.name = name or f"{source}_to_{target}"
|
|
28
|
+
self.priority = priority
|
|
29
|
+
self.tags = tags or []
|
|
30
|
+
self.required_inputs = list(required_inputs) if required_inputs else [source]
|
|
31
|
+
|
|
32
|
+
def __repr__(self):
|
|
33
|
+
return (f"ConversionEntry('{self.source}' -> '{self.target}', "
|
|
34
|
+
f"name='{self.name}', priority={self.priority}, "
|
|
35
|
+
f"required_inputs={self.required_inputs})")
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
class ConversionGraph:
|
|
2
|
+
"""Directed graph of attribute conversions.
|
|
3
|
+
|
|
4
|
+
Vertices are attribute name strings. Edges are ``ConversionEntry``
|
|
5
|
+
objects representing registered conversions from one attribute to
|
|
6
|
+
another. Implemented as a pure adjacency-list with no external
|
|
7
|
+
dependencies.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
def __init__(self):
|
|
11
|
+
self._adjacency = {} # dict[str, list[ConversionEntry]]
|
|
12
|
+
self._by_name = {} # dict[str, ConversionEntry]
|
|
13
|
+
|
|
14
|
+
def add_edge(self, entry):
|
|
15
|
+
"""Add a ConversionEntry as a directed edge.
|
|
16
|
+
|
|
17
|
+
If an entry with the same name already exists it is replaced.
|
|
18
|
+
"""
|
|
19
|
+
if entry.name in self._by_name:
|
|
20
|
+
self.remove_edge(entry.name)
|
|
21
|
+
self._adjacency.setdefault(entry.source, []).append(entry)
|
|
22
|
+
self._by_name[entry.name] = entry
|
|
23
|
+
|
|
24
|
+
def remove_edge(self, name):
|
|
25
|
+
"""Remove an edge by its ConversionEntry.name.
|
|
26
|
+
|
|
27
|
+
Raises ``KeyError`` if the name is not found.
|
|
28
|
+
"""
|
|
29
|
+
if name not in self._by_name:
|
|
30
|
+
raise KeyError(f"No conversion named '{name}'")
|
|
31
|
+
entry = self._by_name.pop(name)
|
|
32
|
+
edges = self._adjacency.get(entry.source, [])
|
|
33
|
+
self._adjacency[entry.source] = [e for e in edges if e.name != name]
|
|
34
|
+
if not self._adjacency[entry.source]:
|
|
35
|
+
del self._adjacency[entry.source]
|
|
36
|
+
|
|
37
|
+
def neighbors(self, source):
|
|
38
|
+
"""Return all ConversionEntry objects from a given source attribute."""
|
|
39
|
+
return list(self._adjacency.get(source, []))
|
|
40
|
+
|
|
41
|
+
def edges(self):
|
|
42
|
+
"""Return all ConversionEntry objects in the graph."""
|
|
43
|
+
return list(self._by_name.values())
|
|
44
|
+
|
|
45
|
+
def has_edge(self, source, target):
|
|
46
|
+
"""Check if any edge exists from source to target."""
|
|
47
|
+
return any(e.target == target
|
|
48
|
+
for e in self._adjacency.get(source, []))
|
|
49
|
+
|
|
50
|
+
def find_edges(self, source, target):
|
|
51
|
+
"""Return all edges from source to target."""
|
|
52
|
+
return [e for e in self._adjacency.get(source, [])
|
|
53
|
+
if e.target == target]
|
|
54
|
+
|
|
55
|
+
def clear(self):
|
|
56
|
+
"""Remove all edges."""
|
|
57
|
+
self._adjacency.clear()
|
|
58
|
+
self._by_name.clear()
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Domain-specific conversion modules.
|
|
2
|
+
|
|
3
|
+
Each sub-package registers its own set of ConversionEntry objects.
|
|
4
|
+
To discover the installed modules, call :func:`list_modules`.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import importlib
|
|
8
|
+
import pkgutil
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def list_modules():
|
|
12
|
+
"""Return the names of all installed sub-packages under ``modules/``."""
|
|
13
|
+
return [name for _, name, is_pkg in pkgutil.iter_modules(__path__)
|
|
14
|
+
if is_pkg]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/output/
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"""Foaming filament conversion registrations.
|
|
2
|
+
|
|
3
|
+
Each entry wraps empirical lookup-table data measured for ColorFabb
|
|
4
|
+
VarioShore TPU and ColorFabb LW-PLA foaming filaments.
|
|
5
|
+
|
|
6
|
+
Usage::
|
|
7
|
+
|
|
8
|
+
import pyvcad_attribute_resolver as resolver
|
|
9
|
+
resolver.register_foaming_conversions() # all 5
|
|
10
|
+
resolver.register_tpu_conversions() # TPU-only (3)
|
|
11
|
+
resolver.register_pla_conversions() # PLA-only (2)
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import pyvcad as pv
|
|
15
|
+
from pyvcad_attribute_resolver.registry import register_conversion
|
|
16
|
+
from .data import (
|
|
17
|
+
TPU_SHORE_HARDNESS,
|
|
18
|
+
TPU_DENSITY,
|
|
19
|
+
TPU_FLOW_COMPENSATION,
|
|
20
|
+
PLA_DENSITY,
|
|
21
|
+
PLA_FLOW_COMPENSATION,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
# ---------------------------------------------------------------------------
|
|
25
|
+
# Helpers
|
|
26
|
+
# ---------------------------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
def _lut_factory(source_attr, target_attr, data):
|
|
29
|
+
"""Return a zero-arg callable that builds a fresh LookupTableConverter."""
|
|
30
|
+
entries = [pv.LookupTableEntry(kv[0], kv[0], kv[1]) for kv in data]
|
|
31
|
+
|
|
32
|
+
def _factory():
|
|
33
|
+
return pv.LookupTableConverter(
|
|
34
|
+
[source_attr], [target_attr],
|
|
35
|
+
entries, pv.InterpolationMode.LINEAR,
|
|
36
|
+
)
|
|
37
|
+
return _factory
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
# ---------------------------------------------------------------------------
|
|
41
|
+
# Public registration functions
|
|
42
|
+
# ---------------------------------------------------------------------------
|
|
43
|
+
|
|
44
|
+
def register_tpu_conversions():
|
|
45
|
+
"""Register the three VarioShore TPU conversions."""
|
|
46
|
+
register_conversion(
|
|
47
|
+
source="shore_hardness", target="temperature",
|
|
48
|
+
converter_factory=_lut_factory(
|
|
49
|
+
pv.DefaultAttributes.SHORE_HARDNESS,
|
|
50
|
+
pv.DefaultAttributes.TEMPERATURE,
|
|
51
|
+
TPU_SHORE_HARDNESS,
|
|
52
|
+
),
|
|
53
|
+
name="tpu_shore_hardness_to_temperature",
|
|
54
|
+
priority=0,
|
|
55
|
+
tags=["foaming_tpu"],
|
|
56
|
+
)
|
|
57
|
+
register_conversion(
|
|
58
|
+
source="density", target="temperature",
|
|
59
|
+
converter_factory=_lut_factory(
|
|
60
|
+
pv.DefaultAttributes.DENSITY,
|
|
61
|
+
pv.DefaultAttributes.TEMPERATURE,
|
|
62
|
+
TPU_DENSITY,
|
|
63
|
+
),
|
|
64
|
+
name="tpu_density_to_temperature",
|
|
65
|
+
priority=0,
|
|
66
|
+
tags=["foaming_tpu"],
|
|
67
|
+
)
|
|
68
|
+
register_conversion(
|
|
69
|
+
source="temperature", target="flow_rate",
|
|
70
|
+
converter_factory=_lut_factory(
|
|
71
|
+
pv.DefaultAttributes.TEMPERATURE,
|
|
72
|
+
pv.DefaultAttributes.FLOW_RATE,
|
|
73
|
+
TPU_FLOW_COMPENSATION,
|
|
74
|
+
),
|
|
75
|
+
name="tpu_temperature_to_flow_rate",
|
|
76
|
+
priority=0,
|
|
77
|
+
tags=["foaming_tpu"],
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def register_pla_conversions():
|
|
82
|
+
"""Register the two LW-PLA conversions."""
|
|
83
|
+
register_conversion(
|
|
84
|
+
source="density", target="temperature",
|
|
85
|
+
converter_factory=_lut_factory(
|
|
86
|
+
pv.DefaultAttributes.DENSITY,
|
|
87
|
+
pv.DefaultAttributes.TEMPERATURE,
|
|
88
|
+
PLA_DENSITY,
|
|
89
|
+
),
|
|
90
|
+
name="pla_density_to_temperature",
|
|
91
|
+
priority=0,
|
|
92
|
+
tags=["foaming_pla"],
|
|
93
|
+
)
|
|
94
|
+
register_conversion(
|
|
95
|
+
source="temperature", target="flow_rate",
|
|
96
|
+
converter_factory=_lut_factory(
|
|
97
|
+
pv.DefaultAttributes.TEMPERATURE,
|
|
98
|
+
pv.DefaultAttributes.FLOW_RATE,
|
|
99
|
+
PLA_FLOW_COMPENSATION,
|
|
100
|
+
),
|
|
101
|
+
name="pla_temperature_to_flow_rate",
|
|
102
|
+
priority=0,
|
|
103
|
+
tags=["foaming_pla"],
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def register_foaming_conversions():
|
|
108
|
+
"""Register all five foaming filament conversions (TPU + PLA)."""
|
|
109
|
+
register_tpu_conversions()
|
|
110
|
+
register_pla_conversions()
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
"""Raw empirical lookup-table data for foaming filaments.
|
|
2
|
+
|
|
3
|
+
The raw constants in this module are the maintained source of truth for the
|
|
4
|
+
foaming filament characterization data. Runtime lookup tables are derived from
|
|
5
|
+
the raw measurements so the conversion data and plot data stay synchronized.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
DESIGNED_WALL_THICKNESS_MM = 0.45
|
|
9
|
+
|
|
10
|
+
# ---------------------------------------------------------------------------
|
|
11
|
+
# ColorFabb VarioShore TPU
|
|
12
|
+
# ---------------------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
TPU_BASE_FLOW_MULTIPLIER = 1.4
|
|
15
|
+
TPU_DENSITY_SAMPLE_VOLUME_CM3 = 6.3
|
|
16
|
+
TPU_FLOW_RUNTIME_TEMPERATURE_RANGE = (204, 240)
|
|
17
|
+
|
|
18
|
+
#: Temperature (C) -> uncompensated wall-thickness measurements (mm)
|
|
19
|
+
TPU_WALL_EXPANSION_RAW = {
|
|
20
|
+
196: [0.41, 0.41, 0.40, 0.40],
|
|
21
|
+
200: [0.43, 0.42, 0.42, 0.42],
|
|
22
|
+
204: [0.41, 0.41, 0.41, 0.40],
|
|
23
|
+
208: [0.43, 0.43, 0.43, 0.43],
|
|
24
|
+
212: [0.48, 0.49, 0.48, 0.47],
|
|
25
|
+
216: [0.55, 0.55, 0.56, 0.56],
|
|
26
|
+
220: [0.67, 0.67, 0.65, 0.65],
|
|
27
|
+
224: [0.74, 0.74, 0.73, 0.73],
|
|
28
|
+
228: [0.84, 0.83, 0.84, 0.84],
|
|
29
|
+
232: [0.87, 0.89, 0.90, 0.89],
|
|
30
|
+
236: [0.91, 0.96, 0.93, 0.92],
|
|
31
|
+
240: [0.95, 0.95, 0.95, 0.94],
|
|
32
|
+
244: [0.94, 0.94, 0.95, 0.97],
|
|
33
|
+
248: [0.97, 0.97, 0.97, 0.95],
|
|
34
|
+
252: [0.99, 1.01, 0.95, 0.91],
|
|
35
|
+
256: [0.99, 0.99, 0.95, 0.95],
|
|
36
|
+
260: [0.99, 0.99, 0.95, 0.96],
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
#: Temperature (C) -> flow factor used during compensated validation
|
|
40
|
+
TPU_WALL_CALIBRATION_FLOW_FACTORS = {
|
|
41
|
+
204: 1.5460122699386503,
|
|
42
|
+
208: 1.4651162790697674,
|
|
43
|
+
212: 1.3125,
|
|
44
|
+
216: 1.1351351351351349,
|
|
45
|
+
220: 0.9545454545454544,
|
|
46
|
+
224: 0.8571428571428571,
|
|
47
|
+
228: 0.7522388059701494,
|
|
48
|
+
232: 0.7098591549295774,
|
|
49
|
+
236: 0.6774193548387096,
|
|
50
|
+
240: 0.6649076517150396,
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
#: Temperature (C) -> compensated wall-thickness measurements (mm)
|
|
54
|
+
TPU_WALL_CALIBRATION_VALIDATION_RAW = {
|
|
55
|
+
204: [0.46, 0.47, 0.43, 0.42],
|
|
56
|
+
208: [0.43, 0.44, 0.41, 0.41],
|
|
57
|
+
212: [0.46, 0.45, 0.46, 0.47],
|
|
58
|
+
216: [0.49, 0.44, 0.47, 0.47],
|
|
59
|
+
220: [0.47, 0.46, 0.47, 0.48],
|
|
60
|
+
224: [0.47, 0.45, 0.45, 0.49],
|
|
61
|
+
228: [0.45, 0.43, 0.45, 0.46],
|
|
62
|
+
232: [0.42, 0.43, 0.44, 0.46],
|
|
63
|
+
236: [0.44, 0.42, 0.44, 0.43],
|
|
64
|
+
240: [0.42, 0.43, 0.45, 0.44],
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
#: Temperature (C) -> density block samples with weight and Shore A repeats
|
|
68
|
+
TPU_SHORE_DENSITY_RAW = {
|
|
69
|
+
196: {"weight_g": 7.1, "shore_hardness": [91.0, 93.5]},
|
|
70
|
+
200: {"weight_g": 7.1, "shore_hardness": [91.0, 93.5]},
|
|
71
|
+
204: {"weight_g": 6.0, "shore_hardness": [90.0, 89.0]},
|
|
72
|
+
208: {"weight_g": 5.6, "shore_hardness": [85.5, 87.5]},
|
|
73
|
+
212: {"weight_g": 5.3, "shore_hardness": [83.0, 86.0]},
|
|
74
|
+
216: {"weight_g": 4.4, "shore_hardness": [76.5, 79.0]},
|
|
75
|
+
220: {"weight_g": 3.5, "shore_hardness": [71.0, 74.0]},
|
|
76
|
+
224: {"weight_g": 3.1, "shore_hardness": [68.5, 69.0]},
|
|
77
|
+
228: {"weight_g": 2.8, "shore_hardness": [65.0, 66.0]},
|
|
78
|
+
232: {"weight_g": 2.6, "shore_hardness": [62.0, 61.0]},
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
# Backward-compatible private name used by older local plotting code.
|
|
82
|
+
_TPU_SHORE_HARDNESS_RAW = {
|
|
83
|
+
temperature: row["shore_hardness"]
|
|
84
|
+
for temperature, row in TPU_SHORE_DENSITY_RAW.items()
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
# ---------------------------------------------------------------------------
|
|
88
|
+
# ColorFabb LW-PLA
|
|
89
|
+
# ---------------------------------------------------------------------------
|
|
90
|
+
|
|
91
|
+
PLA_BASE_FLOW_MULTIPLIER = 1.25
|
|
92
|
+
PLA_DENSITY_SAMPLE_VOLUME_CM3 = 3.7
|
|
93
|
+
PLA_FLOW_RUNTIME_TEMPERATURE_RANGE = (220, 256)
|
|
94
|
+
|
|
95
|
+
#: Temperature (C) -> uncompensated wall-thickness measurements (mm)
|
|
96
|
+
PLA_WALL_EXPANSION_RAW = {
|
|
97
|
+
196: [0.40, 0.40, 0.40, 0.39],
|
|
98
|
+
200: [0.39, 0.39, 0.40, 0.40],
|
|
99
|
+
204: [0.39, 0.40, 0.40, 0.40],
|
|
100
|
+
208: [0.40, 0.41, 0.39, 0.39],
|
|
101
|
+
212: [0.39, 0.39, 0.41, 0.41],
|
|
102
|
+
216: [0.39, 0.39, 0.40, 0.39],
|
|
103
|
+
220: [0.39, 0.40, 0.40, 0.40],
|
|
104
|
+
224: [0.45, 0.45, 0.46, 0.47],
|
|
105
|
+
228: [0.52, 0.52, 0.52, 0.51],
|
|
106
|
+
232: [0.58, 0.57, 0.58, 0.57],
|
|
107
|
+
236: [0.66, 0.65, 0.65, 0.65],
|
|
108
|
+
240: [0.74, 0.73, 0.74, 0.74],
|
|
109
|
+
244: [0.76, 0.80, 0.79, 0.78],
|
|
110
|
+
248: [0.82, 0.82, 0.81, 0.80],
|
|
111
|
+
252: [0.84, 0.83, 0.82, 0.84],
|
|
112
|
+
256: [0.86, 0.86, 0.84, 0.85],
|
|
113
|
+
258: [0.85, 0.84, 0.86, 0.86],
|
|
114
|
+
260: [0.85, 0.83, 0.84, 0.83],
|
|
115
|
+
261: [0.83, 0.82, 0.82, 0.81],
|
|
116
|
+
262: [0.79, 0.82, 0.80, 0.80],
|
|
117
|
+
263: [0.80, 0.80, 0.78, 0.79],
|
|
118
|
+
264: [0.79, 0.78, 0.78, 0.76],
|
|
119
|
+
268: [0.80, 0.77, 0.74, 0.79],
|
|
120
|
+
272: [0.77, 0.76, 0.76, 0.77],
|
|
121
|
+
276: [0.77, 0.74, 0.74, 0.75],
|
|
122
|
+
280: [0.71, 0.73, 0.74, 0.70],
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
#: Temperature (C) -> flow factor used during compensated validation
|
|
126
|
+
PLA_WALL_CALIBRATION_FLOW_FACTORS = {
|
|
127
|
+
220: 1.4150943396226419,
|
|
128
|
+
224: 1.2295081967213115,
|
|
129
|
+
228: 1.0869565217391304,
|
|
130
|
+
232: 0.9782608695652174,
|
|
131
|
+
236: 0.8620689655172414,
|
|
132
|
+
240: 0.7627118644067796,
|
|
133
|
+
244: 0.7188498402555911,
|
|
134
|
+
248: 0.6923076923076923,
|
|
135
|
+
252: 0.6756756756756757,
|
|
136
|
+
256: 0.6598240469208211,
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
#: Temperature (C) -> compensated wall-thickness measurements (mm)
|
|
140
|
+
PLA_WALL_CALIBRATION_VALIDATION_RAW = {
|
|
141
|
+
220: [0.45, 0.46, 0.46, 0.45],
|
|
142
|
+
224: [0.44, 0.44, 0.44, 0.45],
|
|
143
|
+
228: [0.47, 0.46, 0.47, 0.47],
|
|
144
|
+
232: [0.49, 0.48, 0.47, 0.47],
|
|
145
|
+
236: [0.49, 0.48, 0.50, 0.47],
|
|
146
|
+
240: [0.48, 0.48, 0.47, 0.49],
|
|
147
|
+
244: [0.48, 0.47, 0.48, 0.49],
|
|
148
|
+
248: [0.48, 0.45, 0.46, 0.46],
|
|
149
|
+
252: [0.43, 0.44, 0.46, 0.45],
|
|
150
|
+
256: [0.45, 0.45, 0.44, 0.45],
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
#: Temperature (C) -> density block samples with flow factor and weight
|
|
154
|
+
PLA_DENSITY_RAW = {
|
|
155
|
+
216: {"flow_factor": 1.43312101910828, "weight_g": 3.7},
|
|
156
|
+
220: {"flow_factor": 1.4150943396226419, "weight_g": 3.7},
|
|
157
|
+
224: {"flow_factor": 1.2295081967213115, "weight_g": 3.2},
|
|
158
|
+
228: {"flow_factor": 1.0869565217391304, "weight_g": 2.9},
|
|
159
|
+
232: {"flow_factor": 0.9782608695652174, "weight_g": 2.6},
|
|
160
|
+
236: {"flow_factor": 0.8620689655172414, "weight_g": 2.2},
|
|
161
|
+
240: {"flow_factor": 0.7627118644067796, "weight_g": 2.0},
|
|
162
|
+
244: {"flow_factor": 0.7188498402555911, "weight_g": 1.9},
|
|
163
|
+
248: {"flow_factor": 0.6923076923076923, "weight_g": 1.9},
|
|
164
|
+
252: {"flow_factor": 0.6756756756756757, "weight_g": 1.8},
|
|
165
|
+
256: {"flow_factor": 0.6598240469208211, "weight_g": 1.8},
|
|
166
|
+
260: {"flow_factor": 0.671641791044776, "weight_g": 1.8},
|
|
167
|
+
264: {"flow_factor": 0.7234726688102894, "weight_g": 1.8},
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _mean(values):
|
|
172
|
+
return sum(values) / len(values)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def _within_range(value, bounds):
|
|
176
|
+
return bounds[0] <= value <= bounds[1]
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _flow_compensation_from_wall_expansion(raw_data, base_multiplier, bounds):
|
|
180
|
+
rows = []
|
|
181
|
+
for temperature, samples in raw_data.items():
|
|
182
|
+
if _within_range(temperature, bounds):
|
|
183
|
+
flow_rate = (
|
|
184
|
+
base_multiplier
|
|
185
|
+
* DESIGNED_WALL_THICKNESS_MM
|
|
186
|
+
/ _mean(samples)
|
|
187
|
+
)
|
|
188
|
+
rows.append((temperature, flow_rate))
|
|
189
|
+
return sorted(rows)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def _unique_density_to_temperature(rows):
|
|
193
|
+
seen = set()
|
|
194
|
+
unique_rows = []
|
|
195
|
+
for density, temperature in sorted(rows, key=lambda row: row[1]):
|
|
196
|
+
if density in seen:
|
|
197
|
+
continue
|
|
198
|
+
seen.add(density)
|
|
199
|
+
unique_rows.append((density, temperature))
|
|
200
|
+
return sorted(unique_rows)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def _unique_input_rows(rows):
|
|
204
|
+
seen = set()
|
|
205
|
+
unique_rows = []
|
|
206
|
+
for input_value, output_value in sorted(rows, key=lambda row: row[1]):
|
|
207
|
+
if input_value in seen:
|
|
208
|
+
continue
|
|
209
|
+
seen.add(input_value)
|
|
210
|
+
unique_rows.append((input_value, output_value))
|
|
211
|
+
return sorted(unique_rows)
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def _tpu_density_rows():
|
|
215
|
+
rows = []
|
|
216
|
+
for temperature, row in TPU_SHORE_DENSITY_RAW.items():
|
|
217
|
+
density = row["weight_g"] / TPU_DENSITY_SAMPLE_VOLUME_CM3
|
|
218
|
+
rows.append((density, temperature))
|
|
219
|
+
return rows
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def _pla_density_rows():
|
|
223
|
+
rows = []
|
|
224
|
+
for temperature, row in PLA_DENSITY_RAW.items():
|
|
225
|
+
density = row["weight_g"] / PLA_DENSITY_SAMPLE_VOLUME_CM3
|
|
226
|
+
rows.append((density, temperature))
|
|
227
|
+
return rows
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
#: Shore hardness (Shore A) -> temperature (C)
|
|
231
|
+
TPU_SHORE_HARDNESS = _unique_input_rows(
|
|
232
|
+
[
|
|
233
|
+
(_mean(row["shore_hardness"]), temperature)
|
|
234
|
+
for temperature, row in TPU_SHORE_DENSITY_RAW.items()
|
|
235
|
+
]
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
#: Density (g/cm^3) -> temperature (C)
|
|
239
|
+
TPU_DENSITY = _unique_density_to_temperature(_tpu_density_rows())
|
|
240
|
+
|
|
241
|
+
#: Temperature (C) -> flow rate multiplier
|
|
242
|
+
TPU_FLOW_COMPENSATION = _flow_compensation_from_wall_expansion(
|
|
243
|
+
TPU_WALL_EXPANSION_RAW,
|
|
244
|
+
TPU_BASE_FLOW_MULTIPLIER,
|
|
245
|
+
TPU_FLOW_RUNTIME_TEMPERATURE_RANGE,
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
#: Density (g/cm^3) -> temperature (C)
|
|
249
|
+
PLA_DENSITY = _unique_density_to_temperature(_pla_density_rows())
|
|
250
|
+
|
|
251
|
+
#: Temperature (C) -> flow rate multiplier
|
|
252
|
+
PLA_FLOW_COMPENSATION = _flow_compensation_from_wall_expansion(
|
|
253
|
+
PLA_WALL_EXPANSION_RAW,
|
|
254
|
+
PLA_BASE_FLOW_MULTIPLIER,
|
|
255
|
+
PLA_FLOW_RUNTIME_TEMPERATURE_RANGE,
|
|
256
|
+
)
|