Skip to content

Commit 8f37d43

Browse files
committed
Merge branch 'add_volumetric_e' into 'main'
impl volumetric extrusion See merge request kit/fast/lb/collaboration/additive-manufacturing/pygcodedecode!41
2 parents 25ce84b + f225457 commit 8f37d43

File tree

5 files changed

+69
-20
lines changed

5 files changed

+69
-20
lines changed

pyGCodeDecode/data/default_printer_presets.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ prusa_mini_klipper:
4848
debugging:
4949
# general properties
5050
nozzle_diam: 0.4
51-
filament_diam: 1.75
51+
filament_diam: 2.85
52+
volumetric_extrusion: True # true: mm3 with UltiGCode, else mm length
5253
# default settings
5354
p_vel: 85
5455
p_acc: 100

pyGCodeDecode/gcode_interpreter.py

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -577,7 +577,7 @@ def get_values(self, t: float, output_unit_system: str = None) -> Tuple[List[flo
577577

578578
return tmp_vel, tmp_pos
579579

580-
def get_width(self, t: float, extrusion_h: float, filament_dia: float):
580+
def get_width(self, t: float, extrusion_h: float, filament_dia: Optional[float] = None) -> float:
581581
"""Return the extrusion width for a certain extrusion height at time.
582582
583583
Args:
@@ -588,13 +588,14 @@ def get_width(self, t: float, extrusion_h: float, filament_dia: float):
588588
Returns:
589589
float: width
590590
"""
591+
filament_dia = self.initial_machine_setup["filament_diam"] if filament_dia is None else filament_dia
592+
591593
curr_val = self.get_values(t=t)
592594

593595
feed_rate = np.linalg.norm(curr_val[0][:3]) # calculate feed rate at current time
594596
flow_rate = curr_val[0][3] # get extrusion rate at current time
595597

596-
filament_cross_sec = (np.pi * filament_dia**2) / 4 # calculate cross area of filament
597-
598+
filament_cross_sec = np.pi * (filament_dia / 2) ** 2 # calculate cross area of filament
598599
width = (
599600
(flow_rate * filament_cross_sec) / (extrusion_h * feed_rate) if feed_rate > 0 else 0
600601
) # calculate width, zero if no movement.
@@ -622,11 +623,7 @@ def check_initial_setup(self, initial_machine_setup):
622623
"printer_name",
623624
"firmware",
624625
]
625-
optional_keys = [
626-
"layer_cue",
627-
"nozzle_diam",
628-
"filament_diam",
629-
]
626+
optional_keys = ["layer_cue", "nozzle_diam", "filament_diam", "volumetric_extrusion"]
630627

631628
valid_keys = req_keys + optional_keys
632629

pyGCodeDecode/helpers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ def custom_print(*args, lvl=2, **kwargs) -> None:
4545

4646
# print with verbosity level
4747
if lvl <= VERBOSITY_LEVEL:
48-
levels = {3: "[DEBUG]", 2: "[INFO]: ", 1: "[WARNING]: "}
48+
levels = {3: "[DEBUG]:", 2: "[INFO]:", 1: "[WARNING]:"}
4949
prefix = levels.get(lvl, "")
5050
print(prefix, *sanitized_args, **kwargs)
5151

pyGCodeDecode/state_generator.py

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""State generator module."""
22

3+
import math
34
import pathlib
45
import re
56
from typing import List, Match
@@ -81,6 +82,7 @@
8182
default_virtual_machine = {
8283
"absolute_position": True,
8384
"absolute_extrusion": True,
85+
"volumetric_extrusion": False,
8486
"units": "SI (mm)",
8587
"initial_position": None,
8688
# general properties
@@ -207,6 +209,24 @@ def dict_list_traveler(line_dict_list: List[dict], initial_machine_setup: dict)
207209
state_list: (list[state]) all states in a list
208210
209211
"""
212+
213+
def apply_extrusion(line_dict: dict, virtual_machine: dict, command: str) -> dict:
214+
e_value = line_dict[command]["E"]
215+
216+
# volumetric to length conversion
217+
# (1) V = (d/2)^2 * pi * E
218+
# (2) E = V / ((d/2)^2 * pi)
219+
if virtual_machine.get("volumetric_extrusion", False):
220+
# volumetric extrusion
221+
e_value = e_value / (math.pi * (virtual_machine["filament_diam"] / 2) ** 2)
222+
223+
if virtual_machine["absolute_extrusion"]:
224+
virtual_machine["E"] = e_value
225+
else:
226+
virtual_machine["E"] = virtual_machine["E"] + e_value
227+
228+
return virtual_machine
229+
210230
state_list: List[state] = list()
211231

212232
virtual_machine = {
@@ -225,17 +245,15 @@ def dict_list_traveler(line_dict_list: List[dict], initial_machine_setup: dict)
225245
layer_counter = 0
226246

227247
# overwrite default values from initial machine setup
228-
"""TODO: depending on the setting the user should be informed that a default value is used.
229-
I prepared a warning below.
230-
Are all these settings necessary?"""
231248
for key in default_virtual_machine:
232249
if initial_machine_setup is not None and key in initial_machine_setup:
233250
virtual_machine[key] = initial_machine_setup[key]
234251
else:
235-
"""print(
252+
custom_print(
236253
f"The parameter '{key}' was not specified in your machine presets. "
237-
f"Using the the default value of '{default_virtual_machine[key]}' to continue."
238-
)"""
254+
f"Using the the default value of '{default_virtual_machine[key]}' to continue.",
255+
lvl=3,
256+
)
239257
virtual_machine[key] = default_virtual_machine[key]
240258

241259
# initial state creation
@@ -298,10 +316,9 @@ def dict_list_traveler(line_dict_list: List[dict], initial_machine_setup: dict)
298316

299317
# look for extrusion commands and apply abs/rel
300318
if "E" in line_dict[command]:
301-
if virtual_machine["absolute_extrusion"] is True:
302-
virtual_machine["E"] = line_dict[command]["E"]
303-
if virtual_machine["absolute_extrusion"] is False: # redundant
304-
virtual_machine["E"] = virtual_machine["E"] + line_dict[command]["E"]
319+
virtual_machine = apply_extrusion(line_dict, virtual_machine, command)
320+
321+
# feed rates in unit/min to unit/sec
305322
if "F" in line_dict[command]:
306323
virtual_machine["p_vel"] = line_dict[command]["F"] / 60
307324

tests/test_end_to_end.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import pathlib
44

5+
import numpy as np
6+
57

68
def test_end_to_end_compact():
79
"""Testing the simulation functionality with automatic setup, similarly to the brace example."""
@@ -13,6 +15,38 @@ def test_end_to_end_compact():
1315
)
1416

1517

18+
def test_end_to_end_volumetr():
19+
"""Testing the simulation functionality with automatic setup, using volumetric or distance based extrusion."""
20+
from pyGCodeDecode.gcode_interpreter import setup, simulation
21+
22+
preset = setup(pathlib.Path("./tests/data/test_printer_setups.yaml"), "test")
23+
preset.set_property({"volumetric_extrusion": False})
24+
25+
sim = simulation(
26+
gcode_path=pathlib.Path("./tests/data/test_state_generator.gcode"),
27+
initial_machine_setup=preset,
28+
)
29+
30+
end_extrusion = sim.blocklist[-1].segments[-1].pos_end.e
31+
expected_extrusion = 14.0
32+
assert end_extrusion == expected_extrusion, f"Expected {expected_extrusion}, but got {end_extrusion}"
33+
34+
preset = setup(pathlib.Path("./tests/data/test_printer_setups.yaml"), "test")
35+
preset.set_property({"volumetric_extrusion": True})
36+
37+
sim = simulation(
38+
gcode_path=pathlib.Path("./tests/data/test_state_generator.gcode"),
39+
initial_machine_setup=preset,
40+
)
41+
42+
end_extrusion = sim.blocklist[-1].segments[-1].pos_end.e
43+
expected_extrusion = 14.0 / ((1.75 / 2) ** 2 * np.pi)
44+
45+
assert np.isclose(
46+
end_extrusion, expected_extrusion, rtol=1e-9
47+
), f"Expected {expected_extrusion}, but got {end_extrusion}"
48+
49+
1650
def test_end_to_end_extensive():
1751
"""Testing the simulation functionality as well as the various outputs, similarly to the benchy example."""
1852
from pyGCodeDecode.abaqus_file_generator import generate_abaqus_event_series

0 commit comments

Comments
 (0)