|
14 | 14 | abort 'source image does not match the pinned Montage release' unless source.fetch('spec').fetch('image') == 'ovvesley/akoflow-wf-montage:050d' |
15 | 15 | original = source.fetch('spec').fetch('activities') |
16 | 16 | runtimes = CSV.read(File.join(ROOT, 'reference-runtimes.csv'), headers: true) |
17 | | -seconds = runtimes.to_h { |row| [row.fetch('activity_id'), row.fetch('duration_seconds').to_f] } |
| 17 | +# The source timestamps have one-second resolution. One activity completed |
| 18 | +# within the same timestamp second; model it as one second instead of a zero |
| 19 | +# duration, which is not a meaningful schedulable task. |
| 20 | +seconds = runtimes.to_h do |row| |
| 21 | + [row.fetch('activity_id'), [row.fetch('duration_seconds').to_f, 1.0].max] |
| 22 | +end |
18 | 23 | abort 'expected 58 distinct activities with reference runtimes' unless original.length == 58 && original.map { |a| a.fetch('name') }.uniq.length == 58 && (original.map { |a| a['name'] } - seconds.keys).empty? |
19 | 24 |
|
20 | 25 | def output_files(command) |
@@ -60,7 +65,14 @@ def output_files(command) |
60 | 65 | { 'name' => activity['name'], 'runtime' => runtime, 'run' => command, |
61 | 66 | 'workspaceSeedPath' => '/akoflow-wfa-shared', |
62 | 67 | 'cpuLimit' => activity.fetch('cpuLimit'), 'memoryLimit' => activity.fetch('memoryLimit'), |
63 | | - 'dependsOn' => activity.fetch('dependsOn', []) } |
| 68 | + 'dependsOn' => activity.fetch('dependsOn', []), |
| 69 | + # Preserve the per-activity duration observed in the completed Montage |
| 70 | + # reference run. Planning and SimGrid use this deterministic baseline; |
| 71 | + # real execution still records its own observed duration independently. |
| 72 | + 'simulation' => { |
| 73 | + 'model' => 'deterministic', |
| 74 | + 'durationSeconds' => seconds.fetch(activity.fetch('name')) |
| 75 | + } } |
64 | 76 | end |
65 | 77 |
|
66 | 78 | workflow = { 'name' => NAME, 'spec' => { 'namespace' => 'showcase', 'image' => IMAGE, |
|
0 commit comments