mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-10-03 21:38:12 +00:00
Handle cyclic task dependencies in schedule cascading and CPM analysis
This commit is contained in:
@@ -32,7 +32,20 @@ class Usecase:
|
|||||||
self.calendar_cache = {}
|
self.calendar_cache = {}
|
||||||
self.cascade_task(self.settings["task"], is_first_task=True)
|
self.cascade_task(self.settings["task"], is_first_task=True)
|
||||||
|
|
||||||
def cascade_task(self, task, is_first_task=False):
|
def cascade_task(self, task, is_first_task=False, task_sequence=None):
|
||||||
|
if task_sequence is None:
|
||||||
|
task_sequence = []
|
||||||
|
|
||||||
|
if task in task_sequence:
|
||||||
|
print("Warning! Recursive sequence is as follows:")
|
||||||
|
for i, debug_task in enumerate(task_sequence):
|
||||||
|
if i == 0:
|
||||||
|
print("Starting at", debug_task)
|
||||||
|
else:
|
||||||
|
print("... is a predecessor to ...", debug_task)
|
||||||
|
print("... which is cyclically a predecessor to ...", task)
|
||||||
|
raise Exception("Recursive tasks found. Could not cascade schedule.")
|
||||||
|
|
||||||
if not task.TaskTime:
|
if not task.TaskTime:
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -170,7 +183,7 @@ class Usecase:
|
|||||||
)
|
)
|
||||||
|
|
||||||
for rel in task.IsPredecessorTo:
|
for rel in task.IsPredecessorTo:
|
||||||
self.cascade_task(rel.RelatedProcess)
|
self.cascade_task(rel.RelatedProcess, task_sequence=task_sequence + [task])
|
||||||
|
|
||||||
def get_lag_time_days(self, lag_time):
|
def get_lag_time_days(self, lag_time):
|
||||||
return ifcopenshell.util.date.ifc2datetime(lag_time.LagValue.wrappedValue).days
|
return ifcopenshell.util.date.ifc2datetime(lag_time.LagValue.wrappedValue).days
|
||||||
|
|||||||
@@ -39,14 +39,32 @@ class Usecase:
|
|||||||
if not self.start_dates:
|
if not self.start_dates:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
is_cyclic = False
|
||||||
|
attempts = 0
|
||||||
self.pending_nodes = set(self.g.nodes)
|
self.pending_nodes = set(self.g.nodes)
|
||||||
|
max_worst_case_attempts = pow(len(self.pending_nodes), 2)
|
||||||
while self.pending_nodes:
|
while self.pending_nodes:
|
||||||
|
attempts += 1
|
||||||
remaining_nodes = set()
|
remaining_nodes = set()
|
||||||
for pending_node in self.pending_nodes:
|
for pending_node in self.pending_nodes:
|
||||||
if not self.forward_pass(pending_node):
|
if not self.forward_pass(pending_node):
|
||||||
remaining_nodes.add(pending_node)
|
remaining_nodes.add(pending_node)
|
||||||
self.pending_nodes = remaining_nodes
|
self.pending_nodes = remaining_nodes
|
||||||
|
|
||||||
|
# As we parse nodes, the remaining attempts can drop dramatically, so we recalculate the upper limit
|
||||||
|
max_remaining_attempts = pow(len(self.pending_nodes), 2)
|
||||||
|
if max_remaining_attempts < max_worst_case_attempts:
|
||||||
|
max_worst_case_attempts = max_remaining_attempts
|
||||||
|
attempts = 0
|
||||||
|
|
||||||
|
if attempts > max_worst_case_attempts:
|
||||||
|
is_cyclic = True
|
||||||
|
break # We have an infinite loop due to a cyclic graph
|
||||||
|
|
||||||
|
if is_cyclic:
|
||||||
|
raise Exception("Task graph is cyclic and so critical path method cannot be performed.")
|
||||||
|
return
|
||||||
|
|
||||||
self.pending_nodes = set(self.g.nodes)
|
self.pending_nodes = set(self.g.nodes)
|
||||||
while self.pending_nodes:
|
while self.pending_nodes:
|
||||||
remaining_nodes = set()
|
remaining_nodes = set()
|
||||||
|
|||||||
Reference in New Issue
Block a user