Skip to content

Commit 43bca4a

Browse files
committed
Getting Irma.py to work with the scheduler
Run code - Set up code to run the scheduler after everything else in Irma.py was run: tasks, assets, asset_groups (which can be automated later), the task-asset-group matrix, and then dependencies and time offsets - - The dependency offset times are the output of sc.figureOutTaskRelationships - - The task-asset matrix cost and duration values come from task.calcDuration and calcCost and checks each asset group validity Time intervals - Implemented a duration interval in Task.calcDuration() to set the duration to the nearest interval - Task earliest start and finish times are calculated using dependencies, but also to get the last (finish) time to set the number of periods (as a function of the time interval too) - findTaskDependencies now use the time interval too to calculate dt_min - - It also updates to the minimum of time_1_to_2 (still ignoring the other way for now), but ensures that it returns -2.3 over -4.5, since the absolute value is the value we care about here scheduler - updated the wordy parameter to a self parameter - Made a new Gantt chart-style print output (for now)
1 parent eae9888 commit 43bca4a

File tree

3 files changed

+228
-60
lines changed

3 files changed

+228
-60
lines changed

famodel/irma/irma.py

Lines changed: 124 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
from task import Task
4141

4242
from assets import Vessel, Port
43+
from scheduler import Scheduler
4344

4445

4546

@@ -433,7 +434,7 @@ def findCompatibleVessels(self):
433434
pass
434435

435436

436-
def figureOutTaskRelationships(self):
437+
def figureOutTaskRelationships(self, time_interval=0.5):
437438
'''Calculate time constraints
438439
between tasks.
439440
'''
@@ -450,15 +451,15 @@ def figureOutTaskRelationships(self):
450451
for i2, task2 in enumerate(self.tasks.values()):
451452
# look at all action dependencies from tasks 1 to 2 and
452453
# identify the limiting case (the largest time offset)...
453-
dt_min_1_2, dt_min_2_1 = findTaskDependencies(task1, task2)
454+
dt_min_1_2, dt_min_2_1 = findTaskDependencies(task1, task2, time_interval=time_interval)
454455

455456
# for now, just look in one direction
456457
dt_min[i1, i2] = dt_min_1_2
457458

458459
return dt_min
459460

460461

461-
def findTaskDependencies(task1, task2):
462+
def findTaskDependencies(task1, task2, time_interval=0.5):
462463
'''Finds any time dependency between the actions of two tasks.
463464
Returns the minimum time separation required from task 1 to task 2,
464465
and from task 2 to task 1. I
@@ -481,13 +482,23 @@ def findTaskDependencies(task1, task2):
481482
time_2_to_1.append(task2.actions_ti[a2] + act2.duration
482483
- task1.actions_ti[a1])
483484

484-
print(time_1_to_2)
485-
print(time_2_to_1)
485+
#print(time_1_to_2)
486+
#print(time_2_to_1)
486487

487488
# TODO: provide cleaner handling of whether or not there is a time constraint in either direction <<<
488489

489-
dt_min_1_2 = min(time_1_to_2) if time_1_to_2 else -np.inf # minimum time required from t1 start to t2 start
490-
dt_min_2_1 = min(time_2_to_1) if time_2_to_1 else -np.inf # minimum time required from t2 start to t1 start
490+
# Calculate minimum times (rounded to nearest interval)
491+
if time_1_to_2:
492+
raw_dt_min_1_2 = min(time_1_to_2, key=abs)
493+
dt_min_1_2 = np.round(raw_dt_min_1_2 / time_interval) * time_interval
494+
else:
495+
dt_min_1_2 = -np.inf
496+
497+
if time_2_to_1:
498+
raw_dt_min_2_1 = min(time_2_to_1, key=abs)
499+
dt_min_2_1 = np.round(raw_dt_min_2_1 / time_interval) * time_interval
500+
else:
501+
dt_min_2_1 = -np.inf
491502

492503
if dt_min_1_2 + dt_min_2_1 > 0:
493504
print(f"The timing between these two tasks seems to be impossible...")
@@ -545,6 +556,16 @@ def implementStrategy_staged(sc):
545556
sc.addTask('tow_and_hookup', acts, action_sequence='series')
546557

547558

559+
560+
561+
562+
563+
564+
565+
566+
567+
568+
548569
if __name__ == '__main__':
549570
'''This is currently a script to explore how some of the workflow could
550571
work. Can move things into functions/methods as they solidify.
@@ -647,7 +668,7 @@ def implementStrategy_staged(sc):
647668

648669
# create hookup action
649670
a4 = sc.addAction('mooring_hookup', f'mooring_hookup-{mkey}',
650-
objects=[mooring, mooring.attached_to[1]], dependencies=[a2, a3])
671+
objects=[mooring, mooring.attached_to[1]], dependencies=[a3])
651672
#(r=r, mooring=mooring, platform=platform, depends_on=[a4])
652673
# the action creator can record any dependencies related to actions of the platform
653674

@@ -698,10 +719,12 @@ def implementStrategy_staged(sc):
698719

699720

700721
# Example task time adjustment and plot
701-
sc.tasks['tow_and_hookup'].setStartTime(5)
702-
sc.tasks['tow_and_hookup'].chart()
722+
#sc.tasks['tow_and_hookup'].setStartTime(5)
723+
#sc.tasks['tow_and_hookup'].chart()
724+
725+
time_interval = 0.25
703726

704-
#dt_min = sc.figureOutTaskRelationships()
727+
dt_min = sc.figureOutTaskRelationships(time_interval=time_interval)
705728

706729
'''
707730
# inputs for scheduler
@@ -730,8 +753,96 @@ def implementStrategy_staged(sc):
730753
task_asset_matrix[i, :] = row
731754
'''
732755
# ----- Call the scheduler -----
733-
# for timing with weather windows and vessel assignments
756+
# for timing with weather windows and vessel assignments
757+
758+
tasks_scheduler = list(sc.tasks.keys())
759+
760+
for asset in sc.vessels.values():
761+
asset['max_weather'] = asset['transport']['Hs_m']
762+
assets_scheduler = list(sc.vessels.values())
763+
764+
# >>>>> TODO: make this automated to find all possible combinations of "realistic" asset groups
765+
asset_groups_scheduler = [
766+
{'group1': ['AHTS_alpha']},
767+
{'group2': ['CSV_A']},
768+
{'group3': ['AHTS_alpha', 'CSV_A', 'HL_Giant']}
769+
]
770+
771+
task_asset_matrix_scheduler = np.zeros([len(tasks_scheduler), len(asset_groups_scheduler), 2], dtype=int)
772+
for i,task in enumerate(sc.tasks.values()):
773+
for j,asset_group in enumerate(asset_groups_scheduler):
774+
# Extract asset list from the dictionary - values() returns a list containing one list
775+
asset_names = list(asset_group.values())[0]
776+
asset_list = [sc.vessels[asset_name] for asset_name in asset_names]
777+
#task.checkAssets([sc.vessels['AHTS_alpha'], sc.vessels['HL_Giant'], sc.vessels['CSV_A']], display=1)
778+
if not task.checkAssets(asset_list, display=0)[0]:
779+
task_asset_matrix_scheduler[i,j] = (-1, -1)
780+
else:
781+
task.assignAssets(asset_list)
782+
task.calcDuration(duration_interval=time_interval)
783+
task.calcCost()
784+
duration_int = int(round(task.duration / time_interval))
785+
task_asset_matrix_scheduler[i,j] = (task.cost, duration_int)
786+
task.clearAssets()
787+
788+
789+
task_dependencies = {}
790+
dependency_types = {}
791+
offsets = {}
792+
for i, task1 in enumerate(sc.tasks.values()):
793+
for j, task2 in enumerate(sc.tasks.values()):
794+
offset = dt_min[i,j]
795+
if i != j and offset != -np.inf:
796+
if task2.name not in task_dependencies:
797+
task_dependencies[task2.name] = []
798+
task_dependencies[task2.name].append(task1.name)
799+
dependency_types[task1.name + '->' + task2.name] = 'start_start'
800+
offsets[task1.name + '->' + task2.name] = offset / time_interval
801+
802+
for task in sc.tasks.values():
803+
task.calcDuration() # ensure the durations of each task are calculated
804+
805+
task_start_times = {}
806+
task_finish_times = {}
807+
task_list = list(sc.tasks.keys())
808+
809+
for task_name in task_list:
810+
# Find earliest start time based on dependencies
811+
earliest_start = 0
812+
for i, t1_name in enumerate(task_list):
813+
j = task_list.index(task_name)
814+
if i != j and dt_min[i, j] != -np.inf:
815+
# This task depends on t1
816+
earliest_start = max(earliest_start,
817+
task_finish_times.get(t1_name, 0) + dt_min[i, j])
818+
819+
task_start_times[task_name] = earliest_start
820+
task_finish_times[task_name] = earliest_start + sc.tasks[task_name].duration
734821

822+
#weather = np.arange(0, max(task_finish_times.values())+ time_interval, time_interval)
823+
weather = [int(x) for x in np.ones(int(max(task_finish_times.values()) / time_interval), dtype=int)]
824+
825+
scheduler = Scheduler(
826+
tasks=tasks_scheduler,
827+
assets=assets_scheduler,
828+
asset_groups=asset_groups_scheduler,
829+
task_asset_matrix=task_asset_matrix_scheduler,
830+
task_dependencies=task_dependencies,
831+
dependency_types=dependency_types,
832+
offsets=offsets,
833+
weather=weather,
834+
period_duration=time_interval,
835+
wordy=1
836+
)
837+
838+
scheduler.set_up_optimizer()
839+
840+
result = scheduler.optimize()
841+
842+
a = 2
843+
844+
845+
'''
735846
records = []
736847
for task in sc.tasks.values():
737848
print('')
@@ -757,7 +868,7 @@ def implementStrategy_staged(sc):
757868
# print(f"{r['task']} :: {r['action']} duration_hr={r['duration_hr']:.1f} "
758869
# f"start={r['start_hr']:.1f} label='{r['time_label']}' periods={r['periods']}")
759870
760-
871+
'''
761872
# ----- Run the simulation -----
762873
'''
763874
for t in np.arange(8760):

0 commit comments

Comments
 (0)