orca-auto-workflows 6.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. orca_auto/flow/__init__.py +5 -0
  2. orca_auto/flow/_orca_stage_materialization.py +469 -0
  3. orca_auto/flow/_orca_stage_payloads.py +128 -0
  4. orca_auto/flow/activity/__init__.py +1 -0
  5. orca_auto/flow/activity/_workflow_records.py +250 -0
  6. orca_auto/flow/adapters/__init__.py +1 -0
  7. orca_auto/flow/adapters/_engine_adapter_helpers.py +438 -0
  8. orca_auto/flow/adapters/_orca_contract_assembly.py +312 -0
  9. orca_auto/flow/adapters/_orca_contract_context.py +173 -0
  10. orca_auto/flow/adapters/_orca_local_lookup.py +78 -0
  11. orca_auto/flow/adapters/_orca_path_helpers.py +159 -0
  12. orca_auto/flow/adapters/_orca_tracking.py +111 -0
  13. orca_auto/flow/adapters/crest.py +129 -0
  14. orca_auto/flow/adapters/orca.py +49 -0
  15. orca_auto/flow/cli/__init__.py +1 -0
  16. orca_auto/flow/cli/run_dir.py +98 -0
  17. orca_auto/flow/cli/worker_options.py +93 -0
  18. orca_auto/flow/cli/workflow.py +314 -0
  19. orca_auto/flow/cli/workflow_output.py +112 -0
  20. orca_auto/flow/conformer_selection.py +577 -0
  21. orca_auto/flow/contracts/__init__.py +34 -0
  22. orca_auto/flow/contracts/crest.py +205 -0
  23. orca_auto/flow/contracts/orca.py +61 -0
  24. orca_auto/flow/contracts/workflow.py +450 -0
  25. orca_auto/flow/engine_options.py +48 -0
  26. orca_auto/flow/engines/__init__.py +1 -0
  27. orca_auto/flow/engines/crest/__init__.py +1 -0
  28. orca_auto/flow/engines/crest/artifacts.py +211 -0
  29. orca_auto/flow/engines/crest/engine.py +25 -0
  30. orca_auto/flow/engines/crest/execution.py +713 -0
  31. orca_auto/flow/engines/crest/job_inputs.py +129 -0
  32. orca_auto/flow/engines/crest/job_locations.py +62 -0
  33. orca_auto/flow/engines/crest/queue_runtime.py +485 -0
  34. orca_auto/flow/engines/crest/runner.py +751 -0
  35. orca_auto/flow/engines/crest/state.py +78 -0
  36. orca_auto/flow/engines/crest/submission.py +273 -0
  37. orca_auto/flow/engines/crest/terminal.py +212 -0
  38. orca_auto/flow/engines/crest/worker_context.py +116 -0
  39. orca_auto/flow/engines/queue_runtime_common.py +346 -0
  40. orca_auto/flow/engines/scratch.py +210 -0
  41. orca_auto/flow/engines/submission_snapshot.py +71 -0
  42. orca_auto/flow/engines/xtb/__init__.py +1 -0
  43. orca_auto/flow/engines/xtb/artifacts.py +212 -0
  44. orca_auto/flow/engines/xtb/engine.py +26 -0
  45. orca_auto/flow/engines/xtb/execution.py +833 -0
  46. orca_auto/flow/engines/xtb/job_inputs.py +294 -0
  47. orca_auto/flow/engines/xtb/job_locations.py +82 -0
  48. orca_auto/flow/engines/xtb/queue_runtime.py +678 -0
  49. orca_auto/flow/engines/xtb/ranking_artifacts.py +109 -0
  50. orca_auto/flow/engines/xtb/ranking_execution.py +150 -0
  51. orca_auto/flow/engines/xtb/ranking_inputs.py +73 -0
  52. orca_auto/flow/engines/xtb/ranking_models.py +59 -0
  53. orca_auto/flow/engines/xtb/ranking_results.py +145 -0
  54. orca_auto/flow/engines/xtb/ranking_selection.py +166 -0
  55. orca_auto/flow/engines/xtb/runner.py +655 -0
  56. orca_auto/flow/engines/xtb/runner_artifacts.py +145 -0
  57. orca_auto/flow/engines/xtb/runner_ranking.py +75 -0
  58. orca_auto/flow/engines/xtb/state.py +89 -0
  59. orca_auto/flow/engines/xtb/submission.py +332 -0
  60. orca_auto/flow/engines/xtb/terminal.py +390 -0
  61. orca_auto/flow/engines/xtb/worker_context.py +222 -0
  62. orca_auto/flow/engines/xtb/worker_terminal.py +121 -0
  63. orca_auto/flow/manifest.py +573 -0
  64. orca_auto/flow/orca_stage_evidence.py +483 -0
  65. orca_auto/flow/orca_stage_validation.py +228 -0
  66. orca_auto/flow/orchestration/__init__.py +90 -0
  67. orca_auto/flow/orchestration/advance.py +260 -0
  68. orca_auto/flow/orchestration/advance_phases.py +239 -0
  69. orca_auto/flow/orchestration/builders.py +47 -0
  70. orca_auto/flow/orchestration/charge_spin.py +90 -0
  71. orca_auto/flow/orchestration/crest_orca_materialization.py +248 -0
  72. orca_auto/flow/orchestration/factories.py +139 -0
  73. orca_auto/flow/orchestration/interaction_energy_materialization.py +653 -0
  74. orca_auto/flow/orchestration/lifecycle.py +174 -0
  75. orca_auto/flow/orchestration/requests.py +76 -0
  76. orca_auto/flow/orchestration/services.py +142 -0
  77. orca_auto/flow/orchestration/stage_builders.py +259 -0
  78. orca_auto/flow/orchestration/stage_runtime/__init__.py +1 -0
  79. orca_auto/flow/orchestration/stage_runtime/crest.py +229 -0
  80. orca_auto/flow/orchestration/stage_runtime/orca.py +326 -0
  81. orca_auto/flow/orchestration/stage_runtime/shared.py +329 -0
  82. orca_auto/flow/orchestration/stage_view_mutators.py +156 -0
  83. orca_auto/flow/orchestration/stage_views.py +250 -0
  84. orca_auto/flow/orchestration/support.py +77 -0
  85. orca_auto/flow/orchestration/template_builders.py +145 -0
  86. orca_auto/flow/orchestration/workflow_builders.py +322 -0
  87. orca_auto/flow/orchestration/workflow_cancellation.py +572 -0
  88. orca_auto/flow/py.typed +0 -0
  89. orca_auto/flow/registry/__init__.py +55 -0
  90. orca_auto/flow/registry/_markers.py +137 -0
  91. orca_auto/flow/registry/_notifications.py +324 -0
  92. orca_auto/flow/registry/journal.py +290 -0
  93. orca_auto/flow/registry/store.py +587 -0
  94. orca_auto/flow/registry/worker_state_store.py +117 -0
  95. orca_auto/flow/restart/__init__.py +97 -0
  96. orca_auto/flow/restart/mutation.py +398 -0
  97. orca_auto/flow/restart/orca_input.py +517 -0
  98. orca_auto/flow/restart/settings.py +797 -0
  99. orca_auto/flow/restart/stage_ops.py +371 -0
  100. orca_auto/flow/restart/stages.py +153 -0
  101. orca_auto/flow/run_dir/__init__.py +1 -0
  102. orca_auto/flow/run_dir/layout.py +40 -0
  103. orca_auto/flow/run_dir/manifest.py +124 -0
  104. orca_auto/flow/run_dir/options.py +374 -0
  105. orca_auto/flow/runtime/__init__.py +375 -0
  106. orca_auto/flow/runtime/admission.py +178 -0
  107. orca_auto/flow/runtime/advance.py +352 -0
  108. orca_auto/flow/runtime/cycle.py +78 -0
  109. orca_auto/flow/runtime/models.py +145 -0
  110. orca_auto/flow/runtime/results.py +118 -0
  111. orca_auto/flow/scaffold.py +277 -0
  112. orca_auto/flow/stage_event_metadata.py +109 -0
  113. orca_auto/flow/stage_transition_events.py +243 -0
  114. orca_auto/flow/state.py +47 -0
  115. orca_auto/flow/submitters/__init__.py +1 -0
  116. orca_auto/flow/submitters/crest.py +58 -0
  117. orca_auto/flow/submitters/internal_engine_builder.py +65 -0
  118. orca_auto/flow/submitters/internal_engine_cancellation.py +269 -0
  119. orca_auto/flow/submitters/internal_engine_models.py +27 -0
  120. orca_auto/flow/submitters/internal_engine_submission.py +765 -0
  121. orca_auto/flow/submitters/orca.py +246 -0
  122. orca_auto/flow/submitters/xtb.py +76 -0
  123. orca_auto/flow/templates.py +92 -0
  124. orca_auto/flow/workflow/__init__.py +1 -0
  125. orca_auto/flow/workflow/_phases.py +289 -0
  126. orca_auto/flow/workflow/machine.py +310 -0
  127. orca_auto/flow/workflow/notifications.py +317 -0
  128. orca_auto/flow/workflow/report_collection.py +353 -0
  129. orca_auto/flow/workflow/report_diagnostics.py +217 -0
  130. orca_auto/flow/workflow/report_energy_evidence.py +269 -0
  131. orca_auto/flow/workflow/report_rendering.py +388 -0
  132. orca_auto/flow/workflow/si/__init__.py +1 -0
  133. orca_auto/flow/workflow/si/collection.py +1222 -0
  134. orca_auto/flow/workflow/si/publication.py +106 -0
  135. orca_auto/flow/workflow/si/rendering.py +454 -0
  136. orca_auto/flow/workflow/stage_summary.py +115 -0
  137. orca_auto/flow/workflow/status.py +31 -0
  138. orca_auto/flow/workflow/store.py +160 -0
  139. orca_auto/flow/workflow/summary.py +171 -0
  140. orca_auto/flow/xyz_utils.py +505 -0
  141. orca_auto_workflows-6.0.0.dist-info/METADATA +69 -0
  142. orca_auto_workflows-6.0.0.dist-info/RECORD +145 -0
  143. orca_auto_workflows-6.0.0.dist-info/WHEEL +5 -0
  144. orca_auto_workflows-6.0.0.dist-info/licenses/LICENSE +21 -0
  145. orca_auto_workflows-6.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,5 @@
1
+ """Workflow implementation package."""
2
+
3
+ from orca_auto.core.extensions import validate_workflow_installation
4
+
5
+ validate_workflow_installation()
@@ -0,0 +1,469 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, replace
4
+ from pathlib import Path
5
+ from typing import Any
6
+
7
+ from orca_auto.core.app_ids import (
8
+ ORCA_AUTO_CLI_COMMAND,
9
+ ORCA_AUTO_CLI_MODULE,
10
+ ORCA_AUTO_ORCA_SUBMITTER,
11
+ )
12
+ from orca_auto.core.engine_process import (
13
+ atomic_write_confined_bytes,
14
+ ensure_confined_directory,
15
+ require_confined_regular_file,
16
+ )
17
+ from orca_auto.core.utils import atomic_write_json, normalize_text
18
+ from orca_auto.core.utils.coercion import safe_int
19
+ from orca_auto.orca.resource_directives import maxcore_mb_per_core
20
+
21
+ from . import _orca_stage_payloads
22
+ from . import orca_stage_validation as _orca_stage_validation
23
+ from .contracts import WorkflowArtifactRef, WorkflowStage, WorkflowStageInput, WorkflowTask
24
+ from .manifest import require_int
25
+ from .xyz_utils import write_orca_ready_xyz
26
+
27
+
28
+ def safe_name(value: str, *, fallback: str) -> str:
29
+ cleaned = "".join(
30
+ ch if ch.isalnum() or ch in {"_", "-", "."} else "_" for ch in normalize_text(value)
31
+ )
32
+ cleaned = cleaned.strip("._-").lower()
33
+ return cleaned or fallback
34
+
35
+
36
+ def _positive_multiplicity(value: int) -> int:
37
+ return require_int(value, field="multiplicity", minimum=1)
38
+
39
+
40
+ def render_orca_input(
41
+ *,
42
+ route_line: str,
43
+ charge: int,
44
+ multiplicity: int,
45
+ max_cores: int,
46
+ max_memory_gb: int,
47
+ xyz_filename: str,
48
+ ) -> str:
49
+ parsed_charge = require_int(charge, field="charge")
50
+ parsed_multiplicity = _positive_multiplicity(multiplicity)
51
+ return "\n".join(
52
+ [
53
+ _orca_stage_validation.ensure_route_line(route_line),
54
+ "",
55
+ "%pal",
56
+ f" nprocs {max(1, int(max_cores))}",
57
+ "end",
58
+ f"%maxcore {maxcore_mb_per_core(max_memory_gb=max_memory_gb, max_cores=max_cores)}",
59
+ "",
60
+ f"* xyzfile {parsed_charge} {parsed_multiplicity} {xyz_filename}",
61
+ "",
62
+ ]
63
+ )
64
+
65
+
66
+ def build_orca_enqueue_payload(
67
+ *,
68
+ workflow_id: str,
69
+ stage_id: str,
70
+ reaction_dir: str,
71
+ selected_inp: str,
72
+ priority: int,
73
+ resource_request: dict[str, int],
74
+ source_job_id: str,
75
+ reaction_key: str,
76
+ ) -> dict[str, Any]:
77
+ config_placeholder = "<orca_auto_config>"
78
+ resources = _enqueue_resource_values(resource_request)
79
+ command_argv = _orca_enqueue_command_argv(
80
+ config_placeholder=config_placeholder,
81
+ reaction_dir=reaction_dir,
82
+ priority=priority,
83
+ resources=resources,
84
+ )
85
+ command = _orca_enqueue_command(
86
+ config_placeholder=config_placeholder,
87
+ reaction_dir=reaction_dir,
88
+ priority=priority,
89
+ resources=resources,
90
+ )
91
+ return {
92
+ "submitter": ORCA_AUTO_ORCA_SUBMITTER,
93
+ "command": command,
94
+ "command_argv": command_argv,
95
+ "requires_config": True,
96
+ "config_argument_placeholder": config_placeholder,
97
+ "reaction_dir": reaction_dir,
98
+ "selected_inp": selected_inp,
99
+ "priority": int(priority),
100
+ "force": False,
101
+ "max_cores": resources["max_cores"],
102
+ "max_memory_gb": resources["max_memory_gb"],
103
+ "workflow_id": workflow_id,
104
+ "workflow_stage_id": stage_id,
105
+ "source_job_id": source_job_id,
106
+ "reaction_key": reaction_key,
107
+ "resource_request": dict(resource_request),
108
+ }
109
+
110
+
111
+ def _enqueue_resource_values(resource_request: dict[str, int]) -> dict[str, int]:
112
+ return {
113
+ "max_cores": int(resource_request.get("max_cores", 0) or 0),
114
+ "max_memory_gb": int(resource_request.get("max_memory_gb", 0) or 0),
115
+ }
116
+
117
+
118
+ def _resource_override_argv(resources: dict[str, int]) -> list[str]:
119
+ argv: list[str] = []
120
+ if resources["max_cores"] > 0:
121
+ argv.extend(["--max-cores", str(resources["max_cores"])])
122
+ if resources["max_memory_gb"] > 0:
123
+ argv.extend(["--max-memory-gb", str(resources["max_memory_gb"])])
124
+ return argv
125
+
126
+
127
+ def _resource_override_command_parts(resources: dict[str, int]) -> list[str]:
128
+ parts: list[str] = []
129
+ if resources["max_cores"] > 0:
130
+ parts.append(f"--max-cores {resources['max_cores']}")
131
+ if resources["max_memory_gb"] > 0:
132
+ parts.append(f"--max-memory-gb {resources['max_memory_gb']}")
133
+ return parts
134
+
135
+
136
+ def _orca_enqueue_command_argv(
137
+ *,
138
+ config_placeholder: str,
139
+ reaction_dir: str,
140
+ priority: int,
141
+ resources: dict[str, int],
142
+ ) -> list[str]:
143
+ return [
144
+ "python",
145
+ "-m",
146
+ ORCA_AUTO_CLI_MODULE,
147
+ "--config",
148
+ config_placeholder,
149
+ "run-dir",
150
+ reaction_dir,
151
+ "--priority",
152
+ str(int(priority)),
153
+ *_resource_override_argv(resources),
154
+ ]
155
+
156
+
157
+ def _orca_enqueue_command(
158
+ *,
159
+ config_placeholder: str,
160
+ reaction_dir: str,
161
+ priority: int,
162
+ resources: dict[str, int],
163
+ ) -> str:
164
+ command_parts = [
165
+ f"{ORCA_AUTO_CLI_COMMAND} --config {config_placeholder}",
166
+ "run-dir",
167
+ f"'{reaction_dir}'",
168
+ f"--priority {int(priority)}",
169
+ *_resource_override_command_parts(resources),
170
+ ]
171
+ return " ".join(command_parts)
172
+
173
+
174
+ @dataclass(frozen=True)
175
+ class OrcaStageMaterialization:
176
+ reaction_dir: str
177
+ selected_inp: str
178
+ selected_xyz: str
179
+ stage_workspace_dir: str
180
+
181
+
182
+ @dataclass(frozen=True)
183
+ class OrcaStageMaterializationRequest:
184
+ workspace_dir: Path
185
+ stage_key: str
186
+ source_artifact_path: str
187
+ candidate_kind: str
188
+ route_line: str
189
+ charge: int
190
+ multiplicity: int
191
+ max_cores: int
192
+ max_memory_gb: int
193
+ xyz_filename: str = "input.xyz"
194
+ inp_filename: str = "input.inp"
195
+ source_frame_index: int = 0
196
+ extra_source_payload: dict[str, Any] | None = None
197
+ source_artifact_identity: dict[str, Any] | None = None
198
+
199
+
200
+ @dataclass(frozen=True)
201
+ class OrcaStageBuildContext:
202
+ workflow_id: str
203
+ template_name: str
204
+ stage_id: str
205
+ stage_key: str
206
+ workspace_dir: Path
207
+ input_artifact_kind: str
208
+ candidate: WorkflowStageInput
209
+ task_kind: str
210
+ route_line: str
211
+ charge: int
212
+ multiplicity: int
213
+ max_cores: int
214
+ max_memory_gb: int
215
+ priority: int
216
+ xyz_filename: str
217
+ inp_filename: str
218
+ input_label: str | None = None
219
+
220
+ @property
221
+ def resource_request(self) -> dict[str, int]:
222
+ return {
223
+ "max_cores": max(1, int(self.max_cores)),
224
+ "max_memory_gb": max(1, int(self.max_memory_gb)),
225
+ }
226
+
227
+ @property
228
+ def selected_input_label(self) -> str:
229
+ return self.input_label or Path(self.candidate.artifact_path).name
230
+
231
+ def materialization_request(
232
+ self, *, extra_source_payload: dict[str, Any] | None = None
233
+ ) -> OrcaStageMaterializationRequest:
234
+ source_identity_raw = self.candidate.metadata.get("output_identity")
235
+ source_identity = (
236
+ dict(source_identity_raw) if isinstance(source_identity_raw, dict) else None
237
+ )
238
+ return OrcaStageMaterializationRequest(
239
+ workspace_dir=self.workspace_dir,
240
+ stage_key=self.stage_key,
241
+ source_artifact_path=self.candidate.artifact_path,
242
+ candidate_kind=self.candidate.kind,
243
+ route_line=self.route_line,
244
+ charge=self.charge,
245
+ multiplicity=self.multiplicity,
246
+ max_cores=self.max_cores,
247
+ max_memory_gb=self.max_memory_gb,
248
+ xyz_filename=self.xyz_filename,
249
+ inp_filename=self.inp_filename,
250
+ source_frame_index=_candidate_source_frame_index(self.candidate),
251
+ extra_source_payload=extra_source_payload,
252
+ source_artifact_identity=source_identity,
253
+ )
254
+
255
+
256
+ def _candidate_source_frame_index(candidate: WorkflowStageInput) -> int:
257
+ metadata = candidate.metadata if isinstance(candidate.metadata, dict) else {}
258
+ return max(0, safe_int(metadata.get("source_frame_index", 0), default=0))
259
+
260
+
261
+ def materialize_orca_stage_from_request(
262
+ request: OrcaStageMaterializationRequest,
263
+ ) -> OrcaStageMaterialization:
264
+ source_xyz = Path(request.source_artifact_path).expanduser()
265
+ source_xyz = require_confined_regular_file(
266
+ source_xyz.parent,
267
+ source_xyz,
268
+ label="ORCA stage source artifact",
269
+ )
270
+
271
+ stage_dir = _orca_stage_dir(request)
272
+ reaction_dir = ensure_confined_directory(
273
+ request.workspace_dir,
274
+ stage_dir,
275
+ label="ORCA stage directory",
276
+ )
277
+
278
+ target_xyz, geometry_metadata = _materialize_orca_geometry(
279
+ request=request,
280
+ source_xyz=source_xyz,
281
+ reaction_dir=reaction_dir,
282
+ )
283
+ target_inp = _write_orca_input_file(
284
+ request=request,
285
+ reaction_dir=reaction_dir,
286
+ xyz_filename=target_xyz.name,
287
+ )
288
+ extra_source_payload = dict(request.extra_source_payload or {})
289
+ _write_source_candidate_payload(
290
+ stage_dir=stage_dir,
291
+ source_xyz=source_xyz,
292
+ geometry_metadata=geometry_metadata,
293
+ extra_source_payload=extra_source_payload,
294
+ )
295
+ return OrcaStageMaterialization(
296
+ reaction_dir=str(reaction_dir),
297
+ selected_inp=str(target_inp),
298
+ selected_xyz=str(target_xyz),
299
+ stage_workspace_dir=str(stage_dir),
300
+ )
301
+
302
+
303
+ def _orca_stage_dir(request: OrcaStageMaterializationRequest) -> Path:
304
+ return request.workspace_dir / request.stage_key
305
+
306
+
307
+ def _materialize_orca_geometry(
308
+ *,
309
+ request: OrcaStageMaterializationRequest,
310
+ source_xyz: Path,
311
+ reaction_dir: Path,
312
+ ) -> tuple[Path, dict[str, Any]]:
313
+ target_xyz = reaction_dir / request.xyz_filename
314
+ geometry_metadata = write_orca_ready_xyz(
315
+ source_path=source_xyz,
316
+ target_path=target_xyz,
317
+ candidate_kind=request.candidate_kind,
318
+ source_frame_index=request.source_frame_index,
319
+ source_identity=request.source_artifact_identity,
320
+ )
321
+ return target_xyz, dict(geometry_metadata)
322
+
323
+
324
+ def _write_orca_input_file(
325
+ *,
326
+ request: OrcaStageMaterializationRequest,
327
+ reaction_dir: Path,
328
+ xyz_filename: str,
329
+ ) -> Path:
330
+ target_inp = reaction_dir / request.inp_filename
331
+ atomic_write_confined_bytes(
332
+ reaction_dir,
333
+ target_inp,
334
+ render_orca_input(
335
+ route_line=request.route_line,
336
+ charge=request.charge,
337
+ multiplicity=request.multiplicity,
338
+ max_cores=request.max_cores,
339
+ max_memory_gb=request.max_memory_gb,
340
+ xyz_filename=xyz_filename,
341
+ ).encode("utf-8"),
342
+ label="ORCA materialized input",
343
+ )
344
+ return target_inp
345
+
346
+
347
+ def _write_source_candidate_payload(
348
+ *,
349
+ stage_dir: Path,
350
+ source_xyz: Path,
351
+ geometry_metadata: dict[str, Any],
352
+ extra_source_payload: dict[str, Any] | None,
353
+ ) -> None:
354
+ source_payload = {
355
+ "source_artifact_path": str(source_xyz),
356
+ "geometry_materialization": dict(geometry_metadata),
357
+ **dict(extra_source_payload or {}),
358
+ }
359
+ atomic_write_json(
360
+ stage_dir / "source_candidate.json", source_payload, ensure_ascii=True, indent=2
361
+ )
362
+
363
+
364
+ def build_materialized_orca_stage(
365
+ *,
366
+ workflow_id: str,
367
+ template_name: str,
368
+ stage_id: str,
369
+ stage_key: str,
370
+ workspace_dir: Path,
371
+ input_artifact_kind: str,
372
+ candidate: WorkflowStageInput,
373
+ task_kind: str,
374
+ route_line: str,
375
+ charge: int,
376
+ multiplicity: int,
377
+ max_cores: int,
378
+ max_memory_gb: int,
379
+ priority: int,
380
+ xyz_filename: str,
381
+ inp_filename: str,
382
+ input_label: str | None = None,
383
+ ) -> WorkflowStage:
384
+ ctx = OrcaStageBuildContext(
385
+ workflow_id=workflow_id,
386
+ template_name=template_name,
387
+ stage_id=stage_id,
388
+ stage_key=stage_key,
389
+ workspace_dir=workspace_dir,
390
+ input_artifact_kind=input_artifact_kind,
391
+ candidate=candidate,
392
+ task_kind=task_kind,
393
+ route_line=route_line,
394
+ charge=charge,
395
+ multiplicity=multiplicity,
396
+ max_cores=max_cores,
397
+ max_memory_gb=max_memory_gb,
398
+ priority=priority,
399
+ xyz_filename=xyz_filename,
400
+ inp_filename=inp_filename,
401
+ input_label=input_label,
402
+ )
403
+ ctx = replace(
404
+ ctx,
405
+ route_line=_orca_stage_validation.validate_workflow_orca_route(
406
+ task_kind=ctx.task_kind,
407
+ route_line=ctx.route_line,
408
+ ),
409
+ )
410
+ resource_request = ctx.resource_request
411
+ materialized = materialize_orca_stage_from_request(
412
+ ctx.materialization_request(
413
+ extra_source_payload=_orca_stage_payloads.candidate_source_payload(ctx.candidate)
414
+ )
415
+ )
416
+ _orca_stage_validation.validate_workflow_orca_input(
417
+ task_kind=ctx.task_kind,
418
+ inp_path=Path(materialized.selected_inp),
419
+ )
420
+ enqueue_payload = build_orca_enqueue_payload(
421
+ workflow_id=ctx.workflow_id,
422
+ stage_id=ctx.stage_id,
423
+ reaction_dir=materialized.reaction_dir,
424
+ selected_inp=materialized.selected_inp,
425
+ priority=ctx.priority,
426
+ resource_request=resource_request,
427
+ source_job_id=ctx.candidate.source_job_id,
428
+ reaction_key=ctx.candidate.reaction_key,
429
+ )
430
+ task_payload = _orca_stage_payloads.task_payload(
431
+ ctx=ctx,
432
+ materialized=materialized,
433
+ resource_request=resource_request,
434
+ cli_command=ORCA_AUTO_CLI_COMMAND,
435
+ )
436
+ task = _orca_stage_payloads.workflow_task(
437
+ ctx=ctx,
438
+ materialized=materialized,
439
+ resource_request=resource_request,
440
+ enqueue_payload=enqueue_payload,
441
+ task_payload=task_payload,
442
+ workflow_task_cls=WorkflowTask,
443
+ )
444
+ atomic_write_json(
445
+ Path(materialized.stage_workspace_dir) / "enqueue_payload.json",
446
+ enqueue_payload,
447
+ ensure_ascii=True,
448
+ indent=2,
449
+ )
450
+ return _orca_stage_payloads.workflow_stage(
451
+ ctx=ctx,
452
+ materialized=materialized,
453
+ task=task,
454
+ workflow_stage_cls=WorkflowStage,
455
+ artifact_ref_cls=WorkflowArtifactRef,
456
+ )
457
+
458
+
459
+ __all__ = [
460
+ "build_materialized_orca_stage",
461
+ "build_orca_enqueue_payload",
462
+ "materialize_orca_stage_from_request",
463
+ "normalize_text",
464
+ "OrcaStageBuildContext",
465
+ "OrcaStageMaterialization",
466
+ "OrcaStageMaterializationRequest",
467
+ "render_orca_input",
468
+ "safe_name",
469
+ ]
@@ -0,0 +1,128 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Callable
4
+ from typing import Any, TypeVar
5
+
6
+ _WorkflowStageT = TypeVar("_WorkflowStageT")
7
+
8
+
9
+ def candidate_source_payload(candidate: Any) -> dict[str, Any]:
10
+ return {
11
+ "source_job_id": candidate.source_job_id,
12
+ "source_job_type": candidate.source_job_type,
13
+ "reaction_key": candidate.reaction_key,
14
+ "rank": candidate.rank,
15
+ "kind": candidate.kind,
16
+ }
17
+
18
+
19
+ def task_payload(
20
+ *,
21
+ ctx: Any,
22
+ materialized: Any,
23
+ resource_request: dict[str, int],
24
+ cli_command: str,
25
+ ) -> dict[str, Any]:
26
+ return {
27
+ "stage_id": ctx.stage_id,
28
+ "engine": "orca",
29
+ "task_kind": ctx.task_kind,
30
+ "selected_input_xyz": materialized.selected_xyz,
31
+ "selected_input_label": ctx.selected_input_label,
32
+ "source_job_id": ctx.candidate.source_job_id,
33
+ "source_job_type": ctx.candidate.source_job_type,
34
+ "reaction_key": ctx.candidate.reaction_key,
35
+ "workflow_id": ctx.workflow_id,
36
+ "template_name": ctx.template_name,
37
+ "resource_request": dict(resource_request),
38
+ "reaction_dir": materialized.reaction_dir,
39
+ "selected_inp": materialized.selected_inp,
40
+ "suggested_command": f"{cli_command} run-dir '{materialized.reaction_dir}'",
41
+ "metadata": {
42
+ "candidate_rank": ctx.candidate.rank,
43
+ "candidate_kind": ctx.candidate.kind,
44
+ "candidate_score": ctx.candidate.score,
45
+ "candidate_selected": ctx.candidate.selected,
46
+ "candidate_metadata": dict(ctx.candidate.metadata),
47
+ "source_selected_input_xyz": ctx.candidate.selected_input_xyz,
48
+ },
49
+ }
50
+
51
+
52
+ def workflow_task(
53
+ *,
54
+ ctx: Any,
55
+ materialized: Any,
56
+ resource_request: dict[str, int],
57
+ enqueue_payload: dict[str, Any],
58
+ task_payload: dict[str, Any],
59
+ workflow_task_cls: Any,
60
+ ) -> Any:
61
+ return workflow_task_cls.from_raw(
62
+ task_id=f"{ctx.workflow_id}:{ctx.stage_id}",
63
+ engine="orca",
64
+ task_kind=ctx.task_kind,
65
+ resource_request=resource_request,
66
+ payload=task_payload,
67
+ enqueue_payload=enqueue_payload,
68
+ metadata={
69
+ "workflow_id": ctx.workflow_id,
70
+ "template_name": ctx.template_name,
71
+ "source_candidate_path": ctx.candidate.artifact_path,
72
+ "queue_priority": int(ctx.priority),
73
+ "reaction_dir": materialized.reaction_dir,
74
+ "selected_inp": materialized.selected_inp,
75
+ },
76
+ )
77
+
78
+
79
+ def input_artifact_metadata(candidate: Any) -> dict[str, Any]:
80
+ return {
81
+ "rank": candidate.rank,
82
+ "kind": candidate.kind,
83
+ "score": candidate.score,
84
+ **dict(candidate.metadata),
85
+ }
86
+
87
+
88
+ def workflow_stage(
89
+ *,
90
+ ctx: Any,
91
+ materialized: Any,
92
+ task: Any,
93
+ workflow_stage_cls: Callable[..., _WorkflowStageT],
94
+ artifact_ref_cls: Any,
95
+ ) -> _WorkflowStageT:
96
+ return workflow_stage_cls(
97
+ stage_id=ctx.stage_id,
98
+ stage_kind="orca_stage",
99
+ status="planned",
100
+ input_artifacts=(
101
+ artifact_ref_cls(
102
+ kind=ctx.input_artifact_kind,
103
+ path=ctx.candidate.artifact_path,
104
+ selected=ctx.candidate.selected,
105
+ metadata=input_artifact_metadata(ctx.candidate),
106
+ ),
107
+ ),
108
+ output_artifacts=(
109
+ artifact_ref_cls(
110
+ kind="orca_input",
111
+ path=materialized.selected_inp,
112
+ selected=True,
113
+ metadata={
114
+ "engine": "orca",
115
+ "task_kind": ctx.task_kind,
116
+ "reaction_dir": materialized.reaction_dir,
117
+ },
118
+ ),
119
+ ),
120
+ task=task,
121
+ metadata={
122
+ "candidate_rank": ctx.candidate.rank,
123
+ "candidate_kind": ctx.candidate.kind,
124
+ "candidate_score": ctx.candidate.score,
125
+ "selected_input_label": ctx.selected_input_label,
126
+ "reaction_dir": materialized.reaction_dir,
127
+ },
128
+ )
@@ -0,0 +1 @@
1
+ """Workflow-specific activity records for the optional workflow extension."""