mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-20 06:58:56 +00:00
Threads in python app
This commit is contained in:
@@ -220,7 +220,7 @@ int main(int argc, char** argv) {
|
|||||||
("calculate-quantities", "Calculate or fix the physical quantity definitions "
|
("calculate-quantities", "Calculate or fix the physical quantity definitions "
|
||||||
"based on an interpretation of the geometry when exporting IFC");
|
"based on an interpretation of the geometry when exporting IFC");
|
||||||
|
|
||||||
size_t num_threads;
|
int num_threads;
|
||||||
|
|
||||||
po::options_description geom_options("Geometry options");
|
po::options_description geom_options("Geometry options");
|
||||||
geom_options.add_options()
|
geom_options.add_options()
|
||||||
@@ -735,7 +735,7 @@ int main(int argc, char** argv) {
|
|||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (num_threads == 0) {
|
if (num_threads <= 0) {
|
||||||
num_threads = std::thread::hardware_concurrency();
|
num_threads = std::thread::hardware_concurrency();
|
||||||
Logger::Notice("Using " + std::to_string(num_threads) + " threads");
|
Logger::Notice("Using " + std::to_string(num_threads) + " threads");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ namespace IfcGeom {
|
|||||||
namespace {
|
namespace {
|
||||||
template <typename P, typename PP>
|
template <typename P, typename PP>
|
||||||
struct MAKE_TYPE_NAME(factory_t) {
|
struct MAKE_TYPE_NAME(factory_t) {
|
||||||
IfcGeom::IteratorImplementation<P, PP>* operator()(const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector<IfcGeom::filter_t>& filters, size_t num_threads) const {
|
IfcGeom::IteratorImplementation<P, PP>* operator()(const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector<IfcGeom::filter_t>& filters, int num_threads) const {
|
||||||
return new IfcGeom::MAKE_TYPE_NAME(IteratorImplementation_)<P, PP>(settings, file, filters, num_threads);
|
return new IfcGeom::MAKE_TYPE_NAME(IteratorImplementation_)<P, PP>(settings, file, filters, num_threads);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -89,6 +89,8 @@
|
|||||||
#include "../ifcgeom_schema_agnostic/IfcGeomFilter.h"
|
#include "../ifcgeom_schema_agnostic/IfcGeomFilter.h"
|
||||||
#include "../ifcgeom_schema_agnostic/IteratorImplementation.h"
|
#include "../ifcgeom_schema_agnostic/IteratorImplementation.h"
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
// The infamous min & max Win32 #defines can leak here from OCE depending on the build configuration
|
// The infamous min & max Win32 #defines can leak here from OCE depending on the build configuration
|
||||||
#ifdef min
|
#ifdef min
|
||||||
#undef min
|
#undef min
|
||||||
@@ -176,7 +178,8 @@ namespace IfcGeom {
|
|||||||
class MAKE_TYPE_NAME(IteratorImplementation_) : public IteratorImplementation<P, PP> {
|
class MAKE_TYPE_NAME(IteratorImplementation_) : public IteratorImplementation<P, PP> {
|
||||||
private:
|
private:
|
||||||
|
|
||||||
size_t num_threads_;
|
int num_threads_;
|
||||||
|
std::atomic<int> progress_;
|
||||||
std::vector<geometry_conversion_task<P, PP>> tasks_;
|
std::vector<geometry_conversion_task<P, PP>> tasks_;
|
||||||
std::vector<IfcGeom::Element<P, PP>*> all_processed_elements_;
|
std::vector<IfcGeom::Element<P, PP>*> all_processed_elements_;
|
||||||
std::vector<IfcGeom::BRepElement<P, PP>*> all_processed_native_elements_;
|
std::vector<IfcGeom::BRepElement<P, PP>*> all_processed_native_elements_;
|
||||||
@@ -437,10 +440,10 @@ namespace IfcGeom {
|
|||||||
fu.get();
|
fu.get();
|
||||||
|
|
||||||
processed += 1;
|
processed += 1;
|
||||||
const int progress = processed * 50 / tasks_.size();
|
progress_ = processed * 50 / tasks_.size();
|
||||||
if (progress != old_progress) {
|
if (progress_ != old_progress) {
|
||||||
Logger::ProgressBar(progress);
|
Logger::ProgressBar(progress_);
|
||||||
old_progress = progress;
|
old_progress = progress_;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::swap(threadpool[i], threadpool.back());
|
std::swap(threadpool[i], threadpool.back());
|
||||||
@@ -460,10 +463,10 @@ namespace IfcGeom {
|
|||||||
fu.get();
|
fu.get();
|
||||||
|
|
||||||
processed += 1;
|
processed += 1;
|
||||||
const int progress = processed * 50 / tasks_.size();
|
progress_ = processed * 50 / tasks_.size();
|
||||||
if (progress != old_progress) {
|
if (progress_ != old_progress) {
|
||||||
Logger::ProgressBar(progress);
|
Logger::ProgressBar(progress_);
|
||||||
old_progress = progress;
|
old_progress = progress_;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -519,7 +522,13 @@ namespace IfcGeom {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int progress() const { return 100 * done / total; }
|
int progress() const {
|
||||||
|
if (num_threads_ == 1) {
|
||||||
|
return 100 * done / total;
|
||||||
|
} else {
|
||||||
|
return progress_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const std::string& getUnitName() const { return unit_name; }
|
const std::string& getUnitName() const { return unit_name; }
|
||||||
|
|
||||||
@@ -946,7 +955,7 @@ namespace IfcGeom {
|
|||||||
|
|
||||||
bool owns_ifc_file;
|
bool owns_ifc_file;
|
||||||
public:
|
public:
|
||||||
MAKE_TYPE_NAME(IteratorImplementation_)(const IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector<IfcGeom::filter_t>& filters, size_t num_threads)
|
MAKE_TYPE_NAME(IteratorImplementation_)(const IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector<IfcGeom::filter_t>& filters, int num_threads)
|
||||||
: settings(settings)
|
: settings(settings)
|
||||||
, ifc_file(file)
|
, ifc_file(file)
|
||||||
, filters_(filters)
|
, filters_(filters)
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ namespace IfcGeom {
|
|||||||
IteratorImplementation<P, PP>* implementation_;
|
IteratorImplementation<P, PP>* implementation_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Iterator(const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, size_t num_threads = 1)
|
Iterator(const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, int num_threads = 1)
|
||||||
: file_(file)
|
: file_(file)
|
||||||
, settings_(settings)
|
, settings_(settings)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ void IteratorFactoryImplementation<P, PP>::bind(const std::string& schema_name,
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename P, typename PP>
|
template <typename P, typename PP>
|
||||||
IfcGeom::IteratorImplementation<P, PP>* IteratorFactoryImplementation<P, PP>::construct(const std::string& schema_name, const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector<IfcGeom::filter_t>& filters, size_t num_threads) {
|
IfcGeom::IteratorImplementation<P, PP>* IteratorFactoryImplementation<P, PP>::construct(const std::string& schema_name, const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector<IfcGeom::filter_t>& filters, int num_threads) {
|
||||||
const std::string schema_name_lower = boost::to_lower_copy(schema_name);
|
const std::string schema_name_lower = boost::to_lower_copy(schema_name);
|
||||||
typename std::map<std::string, typename get_factory_type<P, PP>::type>::const_iterator it;
|
typename std::map<std::string, typename get_factory_type<P, PP>::type>::const_iterator it;
|
||||||
it = this->find(schema_name_lower);
|
it = this->find(schema_name_lower);
|
||||||
|
|||||||
@@ -23,9 +23,9 @@ namespace IfcGeom {
|
|||||||
class BRepElement;
|
class BRepElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef boost::function4<IfcGeom::IteratorImplementation<float, float>*, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector<IfcGeom::filter_t>&, size_t> iterator_float_float_fn;
|
typedef boost::function4<IfcGeom::IteratorImplementation<float, float>*, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector<IfcGeom::filter_t>&, int> iterator_float_float_fn;
|
||||||
typedef boost::function4<IfcGeom::IteratorImplementation<float, double>*, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector<IfcGeom::filter_t>&, size_t> iterator_float_double_fn;
|
typedef boost::function4<IfcGeom::IteratorImplementation<float, double>*, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector<IfcGeom::filter_t>&, int> iterator_float_double_fn;
|
||||||
typedef boost::function4<IfcGeom::IteratorImplementation<double, double>*, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector<IfcGeom::filter_t>&, size_t> iterator_double_double_fn;
|
typedef boost::function4<IfcGeom::IteratorImplementation<double, double>*, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector<IfcGeom::filter_t>&, int> iterator_double_double_fn;
|
||||||
|
|
||||||
template <typename P, typename PP>
|
template <typename P, typename PP>
|
||||||
struct get_factory_type {};
|
struct get_factory_type {};
|
||||||
@@ -50,7 +50,7 @@ class IteratorFactoryImplementation : public std::map<std::string, typename get_
|
|||||||
public:
|
public:
|
||||||
IteratorFactoryImplementation();
|
IteratorFactoryImplementation();
|
||||||
void bind(const std::string& schema_name, typename get_factory_type<P, PP>::type fn);
|
void bind(const std::string& schema_name, typename get_factory_type<P, PP>::type fn);
|
||||||
IfcGeom::IteratorImplementation<P, PP>* construct(const std::string& schema_name, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector<IfcGeom::filter_t>&, size_t);
|
IfcGeom::IteratorImplementation<P, PP>* construct(const std::string& schema_name, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector<IfcGeom::filter_t>&, int);
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename P, typename PP>
|
template <typename P, typename PP>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import sys
|
|||||||
import time
|
import time
|
||||||
import operator
|
import operator
|
||||||
import functools
|
import functools
|
||||||
|
import multiprocessing
|
||||||
|
|
||||||
import OCC.AIS
|
import OCC.AIS
|
||||||
|
|
||||||
@@ -54,6 +55,43 @@ from .. import version as ifcopenshell_version
|
|||||||
if ifcopenshell_version < "0.6":
|
if ifcopenshell_version < "0.6":
|
||||||
# not yet ported
|
# not yet ported
|
||||||
from .. import get_supertype
|
from .. import get_supertype
|
||||||
|
|
||||||
|
class geometry_creation_signals(QtCore.QObject):
|
||||||
|
completed = QtCore.pyqtSignal('PyQt_PyObject')
|
||||||
|
progress = QtCore.pyqtSignal('PyQt_PyObject')
|
||||||
|
|
||||||
|
class geometry_creation_thread(QtCore.QThread):
|
||||||
|
def __init__(self, signals, settings, f):
|
||||||
|
QtCore.QThread.__init__(self)
|
||||||
|
self.signals = signals
|
||||||
|
self.settings = settings
|
||||||
|
self.f = f
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
t0 = time.time()
|
||||||
|
|
||||||
|
# detect concurrency from hardware, we need to have
|
||||||
|
# at least two threads because otherwise the interface
|
||||||
|
# is different
|
||||||
|
# is different
|
||||||
|
it = iterator(self.settings, self.f, max(2, multiprocessing.cpu_count()))
|
||||||
|
if not it.initialize():
|
||||||
|
self.signals.completed.emit([])
|
||||||
|
return
|
||||||
|
|
||||||
|
def _():
|
||||||
|
|
||||||
|
old_progress = -1
|
||||||
|
while True:
|
||||||
|
shape = it.get()
|
||||||
|
|
||||||
|
if shape:
|
||||||
|
yield shape
|
||||||
|
|
||||||
|
if not it.next():
|
||||||
|
break
|
||||||
|
|
||||||
|
self.signals.completed.emit((it, self.f, list(_())))
|
||||||
|
|
||||||
class configuration(object):
|
class configuration(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@@ -393,62 +431,59 @@ class application(QtWidgets.QApplication):
|
|||||||
self.product_to_ais = {}
|
self.product_to_ais = {}
|
||||||
self.counter = 0
|
self.counter = 0
|
||||||
self.window = widget
|
self.window = widget
|
||||||
|
self.thread = None
|
||||||
|
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
self.InitDriver()
|
self.InitDriver()
|
||||||
self._display.Select = self.HandleSelection
|
self._display.Select = self.HandleSelection
|
||||||
|
|
||||||
def load_file(self, f, setting=None):
|
def finished(self, file_shapes):
|
||||||
|
it, f, shapes = file_shapes
|
||||||
if setting is None:
|
|
||||||
setting = settings()
|
|
||||||
setting.set(setting.USE_PYTHON_OPENCASCADE, True)
|
|
||||||
|
|
||||||
v = self._display
|
v = self._display
|
||||||
|
|
||||||
t = {0: time.time()}
|
t = {0: time.time()}
|
||||||
|
|
||||||
def update(dt=None):
|
def update(dt=None):
|
||||||
t1 = time.time()
|
t1 = time.time()
|
||||||
if t1 - t[0] > (dt or -1):
|
if dt is None or t1 - t[0] > dt:
|
||||||
v.FitAll()
|
v.FitAll()
|
||||||
v.Repaint()
|
v.Repaint()
|
||||||
t[0] = t1
|
t[0] = t1
|
||||||
|
|
||||||
terminate = [False]
|
for shape in shapes:
|
||||||
self.window.window_closed.connect(lambda *args: operator.setitem(terminate, 0, True))
|
|
||||||
|
|
||||||
t0 = time.time()
|
|
||||||
|
|
||||||
it = iterator(setting, f)
|
|
||||||
if not it.initialize():
|
|
||||||
return
|
|
||||||
|
|
||||||
old_progress = -1
|
|
||||||
while True:
|
|
||||||
if terminate[0]:
|
|
||||||
break
|
|
||||||
shape = it.get()
|
|
||||||
product = f[shape.data.id]
|
|
||||||
ais = display_shape(shape, viewer_handle=v)
|
ais = display_shape(shape, viewer_handle=v)
|
||||||
|
product = f[shape.data.id]
|
||||||
|
|
||||||
ais.GetObject().SetSelectionPriority(self.counter)
|
ais.GetObject().SetSelectionPriority(self.counter)
|
||||||
self.ais_to_product[self.counter] = product
|
self.ais_to_product[self.counter] = product
|
||||||
self.product_to_ais[product] = ais
|
self.product_to_ais[product] = ais
|
||||||
self.counter += 1
|
self.counter += 1
|
||||||
|
|
||||||
QtWidgets.QApplication.processEvents()
|
QtWidgets.QApplication.processEvents()
|
||||||
|
|
||||||
if product.is_a() in {'IfcSpace', 'IfcOpeningElement'}:
|
if product.is_a() in {'IfcSpace', 'IfcOpeningElement'}:
|
||||||
v.Context.Erase(ais, True)
|
v.Context.Erase(ais, True)
|
||||||
progress = it.progress() // 2
|
|
||||||
if progress > old_progress:
|
update(1.)
|
||||||
print("\r[" + "#" * progress + " " * (50 - progress) + "]", end="")
|
|
||||||
old_progress = progress
|
|
||||||
if not it.next():
|
|
||||||
break
|
|
||||||
update(0.2)
|
|
||||||
|
|
||||||
print("\rOpened file in %.2f seconds%s" % (time.time() - t0, " " * 25))
|
|
||||||
|
|
||||||
update()
|
update()
|
||||||
|
|
||||||
|
self.thread = None
|
||||||
|
|
||||||
|
def load_file(self, f, setting=None):
|
||||||
|
|
||||||
|
if self.thread is not None:
|
||||||
|
return
|
||||||
|
|
||||||
|
if setting is None:
|
||||||
|
setting = settings()
|
||||||
|
setting.set(setting.USE_PYTHON_OPENCASCADE, True)
|
||||||
|
|
||||||
|
self.signals = geometry_creation_signals()
|
||||||
|
thread = self.thread = geometry_creation_thread(self.signals, setting, f)
|
||||||
|
self.window.window_closed.connect(lambda *args: thread.terminate())
|
||||||
|
self.signals.completed.connect(self.finished)
|
||||||
|
self.thread.start()
|
||||||
|
|
||||||
def select(self, product):
|
def select(self, product):
|
||||||
ais = self.product_to_ais.get(product)
|
ais = self.product_to_ais.get(product)
|
||||||
|
|||||||
@@ -47,9 +47,11 @@ def wrap_shape_creation(settings, shape):
|
|||||||
if has_occ:
|
if has_occ:
|
||||||
from . import occ_utils as utils
|
from . import occ_utils as utils
|
||||||
|
|
||||||
def wrap_shape_creation(settings, shape): return utils.create_shape_from_serialization(shape) if getattr(settings,
|
def wrap_shape_creation(settings, shape):
|
||||||
'use_python_opencascade',
|
if getattr(settings, 'use_python_opencascade', False):
|
||||||
False) else shape
|
return utils.create_shape_from_serialization(shape)
|
||||||
|
else:
|
||||||
|
return shape
|
||||||
|
|
||||||
|
|
||||||
# Subclass the settings module to provide an additional
|
# Subclass the settings module to provide an additional
|
||||||
@@ -77,13 +79,13 @@ _iterator = ifcopenshell_wrapper.iterator_double_precision
|
|||||||
|
|
||||||
# Make sure people are able to use python's platform agnostic paths
|
# Make sure people are able to use python's platform agnostic paths
|
||||||
class iterator(_iterator):
|
class iterator(_iterator):
|
||||||
def __init__(self, settings, file_or_filename):
|
def __init__(self, settings, file_or_filename, num_threads = 1):
|
||||||
self.settings = settings
|
self.settings = settings
|
||||||
if isinstance(file_or_filename, file):
|
if isinstance(file_or_filename, file):
|
||||||
file_or_filename = file_or_filename.wrapped_data
|
file_or_filename = file_or_filename.wrapped_data
|
||||||
else:
|
else:
|
||||||
file_or_filename = os.path.abspath(file_or_filename)
|
file_or_filename = os.path.abspath(file_or_filename)
|
||||||
_iterator.__init__(self, settings, file_or_filename)
|
_iterator.__init__(self, settings, file_or_filename, num_threads)
|
||||||
|
|
||||||
if has_occ:
|
if has_occ:
|
||||||
def get(self):
|
def get(self):
|
||||||
|
|||||||
Reference in New Issue
Block a user