Commit 02ce4015 authored by sumpfralle's avatar sumpfralle

added multiprocessing support for DropCutter and PushCutter


git-svn-id: https://pycam.svn.sourceforge.net/svnroot/pycam/trunk@728 bbaffbd6-741e-11dd-a85d-61de82d9cad9
parent a0c1ad3f
......@@ -25,11 +25,34 @@ from pycam.Geometry.Point import Point
from pycam.Geometry.utils import INFINITE, ceil
from pycam.PathGenerators import get_max_height_triangles, get_max_height_ode
from pycam.Utils import ProgressCounter
from pycam.Utils.threading import run_in_parallel
import pycam.Utils.log
log = pycam.Utils.log.get_logger()
# We need to use a global function here - otherwise it does not work with
# the multiprocessing Pool.
def _process_one_grid_line((positions, minz, maxz, dim_attrs, model, cutter, physics, safety_height)):
# for now only used for triangular collision detection
last_position = None
points = []
height_exceeded = False
for x, y in positions:
if physics:
result = get_max_height_ode(physics, x, y, minz, maxz,
order=dim_attrs[:])
else:
result = get_max_height_triangles(model, cutter, x, y, minz, maxz,
order=dim_attrs[:], last_pos=last_position)
if result:
points.extend(points)
else:
points.append(Point(x, y, safety_height))
height_exceeded = True
return points, height_exceeded
class Dimension:
def __init__(self, start, end):
self.start = float(start)
......@@ -104,10 +127,21 @@ class DropCutter:
self._boundary_warning_already_shown = False
args = []
for one_grid_line in grid:
args.append((one_grid_line, minz, maxz, dim_attrs, self.model,
self.cutter, self.physics, self.model.maxz))
# ODE does not work with multi-threading
disable_multiprocessing = not self.physics is None
for points, height_exceeded in run_in_parallel(_process_one_grid_line,
args, disable_multiprocessing=disable_multiprocessing):
if height_exceeded and not self._boundary_warning_already_shown:
log.warn("DropCutter: exceed the height of the " \
+ "boundary box: using a safe height instead." \
+ " This warning is reported only once.")
self._boundary_warning_already_shown = True
self.pa.new_scanline()
# for now only used for triangular collision detection
last_position = None
if draw_callback and draw_callback(text="DropCutter: processing " \
+ "line %d/%d" % (current_line + 1, num_of_lines)):
......@@ -115,26 +149,8 @@ class DropCutter:
quit_requested = True
break
for x, y in one_grid_line:
if self.physics:
points = get_max_height_ode(self.physics, x, y, minz, maxz,
order=dim_attrs[:])
else:
points = get_max_height_triangles(self.model, self.cutter,
x, y, minz, maxz, order=dim_attrs[:],
last_pos=last_position)
if points:
for p in points:
self.pa.append(p)
else:
p = Point(x, y, self.model.maxz)
self.pa.append(p)
if not self._boundary_warning_already_shown:
log.warn("DropCutter: exceed the height of the " \
+ "boundary box: using a safe height instead." \
+ " This warning is reported only once.")
self._boundary_warning_already_shown = True
self.cutter.moveto(p)
# "draw_callback" returns true, if the user requested to quit
# via the GUI.
......
......@@ -24,10 +24,21 @@ along with PyCAM. If not, see <http://www.gnu.org/licenses/>.
from pycam.Geometry.Point import Point
from pycam.PathGenerators import get_free_paths_ode, get_free_paths_triangles
from pycam.Geometry.utils import epsilon, ceil
from pycam.Utils.threading import run_in_parallel
from pycam.Utils import ProgressCounter
import math
# We need to use a global function here - otherwise it does not work with
# the multiprocessing Pool.
def _process_one_line((p1, p2, depth, model, cutter, physics)):
if physics:
points = get_free_paths_ode(physics, p1, p2, depth=depth)
else:
points = get_free_paths_triangles(model, cutter, p1, p2)
return points
class PushCutter:
def __init__(self, cutter, model, path_processor, physics=None):
......@@ -97,17 +108,17 @@ class PushCutter:
# calculate the required number of steps in each direction
if dx > 0:
depth_x = math.log(accuracy * abs(maxx - minx) / dx) / math.log(2)
depth_x = max(ceil(depth_x), 4)
depth_x = min(depth_x, max_depth)
depth = math.log(accuracy * abs(maxx - minx) / dx) / math.log(2)
depth = max(ceil(depth), 4)
depth = min(depth, max_depth)
num_of_x_lines = 1 + ceil(abs(maxx - minx) / dx)
x_step = abs(maxx - minx) / max(1, (num_of_x_lines - 1))
x_steps = [minx + i * x_step for i in range(num_of_x_lines)]
y_steps = [None] * num_of_x_lines
elif dy != 0:
depth_y = math.log(accuracy * abs(maxy - miny) / dy) / math.log(2)
depth_y = max(ceil(depth_y), 4)
depth_y = min(depth_y, max_depth)
depth = math.log(accuracy * abs(maxy - miny) / dy) / math.log(2)
depth = max(ceil(depth), 4)
depth = min(depth, max_depth)
num_of_y_lines = 1 + ceil(abs(maxy - miny) / dy)
y_step = abs(maxy - miny) / max(1, (num_of_y_lines - 1))
y_steps = [miny + i * y_step for i in range(num_of_y_lines)]
......@@ -116,37 +127,30 @@ class PushCutter:
# nothing to be done
return
args = []
if dx > 0:
depth = depth_
for x, y in zip(x_steps, y_steps):
self.pa.new_scanline()
if dx > 0:
p1, p2 = Point(x, miny, z), Point(x, maxy, z)
if self.physics:
points = get_free_paths_ode(self.physics, p1, p2,
depth=depth_x)
else:
points = get_free_paths_triangles(self.model, self.cutter,
p1, p2)
else:
p1, p2 = Point(minx, y, z), Point(maxx, y, z)
if self.physics:
points = get_free_paths_ode(self.physics, p1, p2,
depth=depth_y)
else:
points = get_free_paths_triangles(self.model, self.cutter,
p1, p2)
args.append((p1, p2, depth, self.model, self.cutter, self.physics))
# ODE does not work with multi-threading
disable_multiprocessing = not self.physics is None
for points in run_in_parallel(_process_one_line, args,
disable_multiprocessing=disable_multiprocessing):
if points:
self.pa.new_scanline()
for p in points:
self.pa.append(p)
self.cutter.moveto(p)
if draw_callback:
draw_callback(tool_position=p, toolpath=self.pa.paths)
self.pa.end_scanline()
# update the progress counter
if not progress_counter is None:
if progress_counter.increment():
if progress_counter and progress_counter.increment():
# quit requested
break
......@@ -20,7 +20,7 @@ You should have received a copy of the GNU General Public License
along with PyCAM. If not, see <http://www.gnu.org/licenses/>.
"""
__all__ = [ "iterators", "polynomials", "ProgressCounter"]
__all__ = [ "iterators", "polynomials", "ProgressCounter", "threading"]
import os
# this is imported below on demand
......
......@@ -65,12 +65,12 @@ def init_threading(number_of_processes=None):
log.info("Enabled multi-threading with %d parallel processes" % __num_of_processes)
def run_in_parallel(func, args, unordered=False):
def run_in_parallel(func, args, unordered=False, disable_multiprocessing=False):
global __multiprocessing, __num_of_processes
if __multiprocessing is None:
# threading was not configured before
init_threading()
if __multiprocessing:
if __multiprocessing and not disable_multiprocessing:
# use the number of CPUs as the default number of worker threads
pool = __multiprocessing.Pool(__num_of_processes)
if unordered:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment