Skip to content

Commit 68d8550

Browse files
authored
Mimas: more flexible/exensible handling of scenario (#131)
Use an entry points mechanism to allow enable a more flexible and extensible approach to handling scenarios in mimas. The simplest mechanism is to register one (or more) functions that take a scenario and return a list of MimasRecipeInvocations or MimasISPyBJobInvocations: def handle_scenarios(scenario: MimasScenario) -> List[Invocation]: return [ MimasRecipeInvocation(...), MimasISPyBJobInvocation(...), ... ] This function will handle every scenario passed to the mimas service. Alternatively, a more modular approach can be used, using the @match_specification decorator to filter scenarios that match a given specification before passing them to the function: from dlstbx.mimas.specification import BeamlineSpecification, DCClassSpecification is_i99 = BeamlineSpecification("i99") is_rotation = DCClassSpecification(MimasDCClass.ROTATION) @match_specification(is_i99 & is_rotation) def handle_i99_rotation(scenario: MimasScenario) -> List[Invocation]: return [ MimasRecipeInvocation(...), MimasISPyBJobInvocation(...), ... ] Various commonly used specifications are provided, such as BeamlineSpecification, DCClassSpecification, DetectorClassSpecification, EventSpecification andVisitSpecification. More complex custom specifications may be built by inheriting from ScenarioSpecification. Specifications can be combined using bitwise operators (&, |, ~).
1 parent 9c2c9d5 commit 68d8550

13 files changed

Lines changed: 844 additions & 756 deletions

File tree

setup.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,27 @@
160160
"zocalo.dlq.rabbitmq = dlstbx.health_checks.rabbitmq:check_rabbitmq_dlq",
161161
]
162162

163+
mimas_scenario_handlers = [
164+
"cloud = dlstbx.mimas.cloud:handle_cloud",
165+
"eiger_screening = dlstbx.mimas.core:handle_eiger_screening",
166+
"eiger_start = dlstbx.mimas.core:handle_eiger_start",
167+
"eiger_end = dlstbx.mimas.core:handle_eiger_end",
168+
"i19_pilatus_start = dlstbx.mimas.i19:handle_i19_start_pilatus",
169+
"i19_eiger_start = dlstbx.mimas.i19:handle_i19_start_eiger",
170+
"i19_pilatus_end = dlstbx.mimas.i19:handle_i19_end_pilatus",
171+
"i19_eiger_end = dlstbx.mimas.i19:handle_i19_end_eiger",
172+
"i19_end = dlstbx.mimas.i19:handle_i19_end",
173+
"pilatus_end = dlstbx.mimas.core:handle_pilatus_end",
174+
"pilatus_gridscan_start = dlstbx.mimas.core:handle_pilatus_gridscan_start",
175+
"pilatus_not_gridscan_start = dlstbx.mimas.core:handle_pilatus_not_gridscan_start",
176+
"pilatus_screening = dlstbx.mimas.core:handle_pilatus_screening",
177+
"rotation_end = dlstbx.mimas.core:handle_rotation_end",
178+
"vmxi_end = dlstbx.mimas.vmxi:handle_vmxi_end",
179+
"vmxi_gridscan = dlstbx.mimas.vmxi:handle_vmxi_gridscan",
180+
"vmxi_rotation = dlstbx.mimas.vmxi:handle_vmxi_rotation_scan",
181+
"vmxi_start = dlstbx.mimas.vmxi:handle_vmxi_start",
182+
]
183+
163184

164185
def get_git_revision():
165186
"""Try to obtain the current git revision number"""
@@ -219,6 +240,7 @@ def get_git_revision():
219240
"thumbnail = dlstbx.services.images:thumbnail",
220241
],
221242
"zocalo.wrappers": sorted(known_wrappers),
243+
"zocalo.mimas.handlers": sorted(mimas_scenario_handlers),
222244
},
223245
packages=find_packages("src"),
224246
package_dir={"": "src"},

src/dlstbx/cli/mimas.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
import argparse
1313

1414
import dlstbx.ispybtbx
15-
import dlstbx.mimas.core
1615

1716
_readable = {
1817
dlstbx.mimas.MimasEvent.START: "start of data collection",
@@ -104,7 +103,7 @@ def run(args=None):
104103

105104
for dcid in args.dcids:
106105
for scenario in get_scenarios(dcid):
107-
actions = dlstbx.mimas.core.run(scenario)
106+
actions = dlstbx.mimas.handle_scenario(scenario)
108107
print(f"At the {_readable.get(scenario.event)} {dcid}:")
109108
for a in sorted(actions, key=lambda a: str(type(a)) + " " + a.recipe):
110109
try:

src/dlstbx/mimas/__init__.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
1+
from __future__ import annotations
2+
13
import dataclasses
24
import enum
35
import functools
46
import numbers
5-
from typing import Optional, Tuple
7+
from typing import Callable, List, Optional, Tuple, Union
68

79
import gemmi
10+
import pkg_resources
11+
12+
from dlstbx.mimas.specification import BaseSpecification
813

914
MimasDCClass = enum.Enum("MimasDCClass", "GRIDSCAN ROTATION SCREENING UNDEFINED")
1015

@@ -373,3 +378,34 @@ def _(mimasobject: MimasISPyBJobInvocation):
373378
*triggervars,
374379
)
375380
)
381+
382+
383+
Invocation = Union[MimasISPyBJobInvocation, MimasRecipeInvocation]
384+
385+
386+
def match_specification(specification: BaseSpecification):
387+
def outer_wrapper(handler: Callable):
388+
@functools.wraps(handler)
389+
def inner_wrapper(scenario: MimasScenario) -> List[Invocation]:
390+
if specification.is_satisfied_by(scenario):
391+
return handler(scenario)
392+
return []
393+
394+
return inner_wrapper
395+
396+
return outer_wrapper
397+
398+
399+
@functools.lru_cache
400+
def _get_handlers() -> dict[str, Callable]:
401+
return {
402+
e.name: e.load()
403+
for e in pkg_resources.iter_entry_points("zocalo.mimas.handlers")
404+
}
405+
406+
407+
def handle_scenario(scenario: MimasScenario) -> List[Invocation]:
408+
tasks: List[Invocation] = []
409+
for handler in _get_handlers().values():
410+
tasks.extend(handler(scenario))
411+
return tasks

src/dlstbx/mimas/cloud.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
from __future__ import annotations
2+
3+
from typing import List
4+
5+
from dlstbx import mimas
6+
from dlstbx.mimas.core import is_end, is_rotation, xia2_dials_absorption_params
7+
from dlstbx.mimas.specification import BeamlineSpecification, VisitSpecification
8+
9+
CLOUD_VISITS = {
10+
"cm",
11+
"nt28218",
12+
"mx",
13+
}
14+
15+
16+
is_cloud = (
17+
VisitSpecification(CLOUD_VISITS)
18+
& is_end
19+
& is_rotation
20+
& BeamlineSpecification("i03")
21+
)
22+
23+
24+
@mimas.match_specification(is_cloud)
25+
def handle_cloud(
26+
scenario: mimas.MimasScenario,
27+
) -> List[mimas.Invocation]:
28+
return [
29+
# xia2-dials
30+
mimas.MimasISPyBJobInvocation(
31+
DCID=scenario.DCID,
32+
autostart=True,
33+
recipe="autoprocessing-xia2-dials-eiger-cloud",
34+
source="automatic",
35+
parameters=(
36+
mimas.MimasISPyBParameter(
37+
key="resolution.cc_half_significance_level", value="0.1"
38+
),
39+
*xia2_dials_absorption_params(scenario),
40+
),
41+
),
42+
# xia2-3dii
43+
mimas.MimasISPyBJobInvocation(
44+
DCID=scenario.DCID,
45+
autostart=True,
46+
recipe="autoprocessing-xia2-3dii-eiger-cluster",
47+
source="automatic",
48+
parameters=(
49+
mimas.MimasISPyBParameter(
50+
key="resolution.cc_half_significance_level", value="0.1"
51+
),
52+
),
53+
),
54+
# autoPROC
55+
mimas.MimasISPyBJobInvocation(
56+
DCID=scenario.DCID,
57+
autostart=True,
58+
recipe="autoprocessing-autoPROC-eiger-cluster",
59+
source="automatic",
60+
),
61+
]

0 commit comments

Comments
 (0)