restage 0.3.2__tar.gz → 0.4.1__tar.gz

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 (36) hide show
  1. {restage-0.3.2/src/restage.egg-info → restage-0.4.1}/PKG-INFO +12 -4
  2. {restage-0.3.2 → restage-0.4.1}/pyproject.toml +11 -4
  3. {restage-0.3.2 → restage-0.4.1}/src/restage/bifrost_choppers.py +6 -5
  4. {restage-0.3.2 → restage-0.4.1}/src/restage/cache.py +2 -1
  5. {restage-0.3.2 → restage-0.4.1}/src/restage/energy.py +11 -6
  6. {restage-0.3.2 → restage-0.4.1}/src/restage/instr.py +4 -2
  7. {restage-0.3.2 → restage-0.4.1}/src/restage/splitrun.py +50 -56
  8. {restage-0.3.2 → restage-0.4.1}/src/restage/tables.py +2 -2
  9. {restage-0.3.2 → restage-0.4.1/src/restage.egg-info}/PKG-INFO +12 -4
  10. {restage-0.3.2 → restage-0.4.1}/src/restage.egg-info/SOURCES.txt +0 -2
  11. restage-0.4.1/src/restage.egg-info/requires.txt +8 -0
  12. {restage-0.3.2 → restage-0.4.1}/test/test_energy.py +36 -28
  13. {restage-0.3.2 → restage-0.4.1}/test/test_single.py +71 -18
  14. restage-0.3.2/src/restage.egg-info/requires.txt +0 -11
  15. restage-0.3.2/test/splitRunTest_first.c +0 -12419
  16. restage-0.3.2/test/splitRunTest_second.c +0 -10944
  17. {restage-0.3.2 → restage-0.4.1}/.github/workflows/pip.yml +0 -0
  18. {restage-0.3.2 → restage-0.4.1}/.github/workflows/wheels.yml +0 -0
  19. {restage-0.3.2 → restage-0.4.1}/.gitignore +0 -0
  20. {restage-0.3.2 → restage-0.4.1}/README.md +0 -0
  21. {restage-0.3.2 → restage-0.4.1}/setup.cfg +0 -0
  22. {restage-0.3.2 → restage-0.4.1}/src/restage/__init__.py +0 -0
  23. {restage-0.3.2 → restage-0.4.1}/src/restage/cspec_choppers.py +0 -0
  24. {restage-0.3.2 → restage-0.4.1}/src/restage/database.py +0 -0
  25. {restage-0.3.2 → restage-0.4.1}/src/restage/emulate.py +0 -0
  26. {restage-0.3.2 → restage-0.4.1}/src/restage/mcpl.py +0 -0
  27. {restage-0.3.2 → restage-0.4.1}/src/restage/range.py +0 -0
  28. {restage-0.3.2 → restage-0.4.1}/src/restage/run.py +0 -0
  29. {restage-0.3.2 → restage-0.4.1}/src/restage/scan.py +0 -0
  30. {restage-0.3.2 → restage-0.4.1}/src/restage.egg-info/dependency_links.txt +0 -0
  31. {restage-0.3.2 → restage-0.4.1}/src/restage.egg-info/entry_points.txt +0 -0
  32. {restage-0.3.2 → restage-0.4.1}/src/restage.egg-info/top_level.txt +0 -0
  33. {restage-0.3.2 → restage-0.4.1}/test/test_cache.py +0 -0
  34. {restage-0.3.2 → restage-0.4.1}/test/test_database.py +0 -0
  35. {restage-0.3.2 → restage-0.4.1}/test/test_range.py +0 -0
  36. {restage-0.3.2 → restage-0.4.1}/test/test_scan.py +0 -0
@@ -1,14 +1,22 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: restage
3
- Version: 0.3.2
3
+ Version: 0.4.1
4
4
  Author-email: Gregory Tucker <gregory.tucker@ess.eu>
5
+ License: BSD-3-Clause
5
6
  Classifier: License :: OSI Approved :: BSD License
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Programming Language :: Python :: 3 :: Only
9
+ Classifier: Programming Language :: Python :: 3.9
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Requires-Python: >=3.9
6
15
  Description-Content-Type: text/markdown
7
16
  Requires-Dist: zenlog>=1.1
8
17
  Requires-Dist: platformdirs>=3.11
9
18
  Requires-Dist: psutil>=5.9.6
10
- Requires-Dist: importlib_metadata; python_version < "3.8"
11
- Requires-Dist: mccode-antlr[hdf5]>=0.7.1
19
+ Requires-Dist: mccode-antlr[hdf5]>=0.10.2
12
20
  Provides-Extra: test
13
21
  Requires-Dist: pytest; extra == "test"
14
22
  Requires-Dist: chopcal; extra == "test"
@@ -8,15 +8,23 @@ dependencies = [
8
8
  'zenlog>=1.1',
9
9
  'platformdirs>=3.11',
10
10
  'psutil>=5.9.6',
11
- "importlib_metadata; python_version<'3.8'",
12
- 'mccode-antlr[hdf5]>=0.7.1',
11
+ 'mccode-antlr[hdf5]>=0.10.2',
13
12
  ]
14
13
  readme = "README.md"
14
+ license = {text = "BSD-3-Clause"}
15
+ requires-python = ">=3.9"
15
16
  authors = [
16
17
  { name = "Gregory Tucker", email = "gregory.tucker@ess.eu" },
17
18
  ]
18
19
  classifiers = [
19
20
  "License :: OSI Approved :: BSD License",
21
+ "Programming Language :: Python :: 3",
22
+ "Programming Language :: Python :: 3 :: Only",
23
+ "Programming Language :: Python :: 3.9",
24
+ "Programming Language :: Python :: 3.10",
25
+ "Programming Language :: Python :: 3.11",
26
+ "Programming Language :: Python :: 3.12",
27
+ "Programming Language :: Python :: 3.13",
20
28
  ]
21
29
  dynamic = ["version"]
22
30
 
@@ -35,10 +43,9 @@ legacy_tox_ini = """
35
43
  [tox]
36
44
  min_version = 4.0
37
45
  env_list =
46
+ py313
38
47
  py312
39
48
  py311
40
- py310
41
- py39
42
49
  type
43
50
 
44
51
  [testenv]
@@ -82,9 +82,7 @@ def bandwidth_chopper_speeds_phases(energy_minimum: float):
82
82
  return SOURCE_FREQUENCY, phase, -SOURCE_FREQUENCY, phase
83
83
 
84
84
 
85
- def calculate(order: float, time: float, energy: float, names: list[str] | None = None):
86
- if names is None or len(names) != 6:
87
- names = ['ps1', 'ps2', 'fo1', 'fo2', 'bw1', 'bw2']
85
+ def calculate(order: float, time: float, energy: float, names: tuple[str, ...]):
88
86
  a, b, c, d, e, f = names
89
87
  s, p = 'speed', 'phase'
90
88
  r = dict()
@@ -94,9 +92,12 @@ def calculate(order: float, time: float, energy: float, names: list[str] | None
94
92
  return r
95
93
 
96
94
 
97
- def main(order: float, time: float, energy: float, names: list[str] | None = None):
95
+ def main(order: float, time: float, energy: float, names: tuple[str, ...] | None = None):
98
96
  if names is None or len(names) != 6:
99
- names = ['ps1', 'ps2', 'fo1', 'fo2', 'bw1', 'bw2']
97
+ # names = ('ps1', 'ps2', 'fo1', 'fo2', 'bw1', 'bw2')
98
+ names = ('pulse_shaping_chopper_1', 'pulse_shaping_chopper_2',
99
+ 'frame_overlap_chopper_1', 'frame_overlap_chopper_2',
100
+ 'bandwidth_chopper_1', 'bandwidth_chopper_2')
100
101
  rep = calculate(order, time, energy, names)
101
102
  print(' '.join([f'{k}={v}' for k, v in rep.items()]))
102
103
 
@@ -2,7 +2,6 @@ from __future__ import annotations
2
2
 
3
3
  from mccode_antlr.instr import Instr
4
4
  from .tables import InstrEntry, SimulationTableEntry, SimulationEntry
5
- from mccode_antlr.compiler.c import CBinaryTarget
6
5
 
7
6
 
8
7
  def setup_database(named: str):
@@ -54,6 +53,8 @@ def _compile_instr(entry: InstrEntry, instr: Instr, config: dict | None = None,
54
53
  generator = MCSTAS_GENERATOR
55
54
 
56
55
  output = directory_under_module_data_path('bin')
56
+ # TODO consider adding `dump_source=True` _and_ putting the resulting file into
57
+ # the cache in order to make debugging future problems a tiny bit easier.
57
58
  binary_path = compile_instrument(instr, target, output, generator=generator, config=config)
58
59
  entry.mccode_version = __version__
59
60
  entry.binary_path = str(binary_path)
@@ -10,13 +10,15 @@ def get_and_remove(d: dict, k: str, default=None):
10
10
  return default
11
11
 
12
12
 
13
- def one_generic_energy_to_chopper_parameters(calculate_choppers, time: float, order: int, parameters: dict):
13
+ def one_generic_energy_to_chopper_parameters(
14
+ calculate_choppers, chopper_names: tuple[str, ...],
15
+ time: float, order: int, parameters: dict):
14
16
  if any(x in parameters for x in ('ei', 'wavelength', 'lambda', 'energy', 'e')):
15
17
  ei = get_and_remove(parameters, 'ei', get_and_remove(parameters, 'energy', get_and_remove(parameters, 'e')))
16
18
  if ei is None:
17
19
  wavelength = get_and_remove(parameters, 'wavelength', get_and_remove(parameters, 'lambda'))
18
20
  ei = _wavelength_angstrom_to_energy_mev(wavelength)
19
- choppers = calculate_choppers(order, time, ei)
21
+ choppers = calculate_choppers(order, time, ei, names=chopper_names)
20
22
  parameters.update(choppers)
21
23
  return parameters
22
24
 
@@ -24,25 +26,28 @@ def one_generic_energy_to_chopper_parameters(calculate_choppers, time: float, or
24
26
  def bifrost_translate_energy_to_chopper_parameters(parameters: dict):
25
27
  from itertools import product
26
28
  from .bifrost_choppers import calculate
27
- for name in product([a+b for a, b in product(('ps', 'fo', 'bw'), ('1', '2'))], ('speed', 'phase')):
29
+ choppers = tuple(f'{a}_chopper_{b}' for a, b in product(['pulse_shaping', 'frame_overlap', 'bandwidth'], [1, 2]))
30
+ # names = [a+b for a, b in product(('ps', 'fo', 'bw'), ('1', '2'))]
31
+ for name in product(choppers, ('speed', 'phase')):
28
32
  name = ''.join(name)
29
33
  if name not in parameters:
30
34
  parameters[name] = 0
31
35
  order = get_and_remove(parameters, 'order', 14)
32
36
  time = get_and_remove(parameters, 'time', get_and_remove(parameters, 't', 170/180/(2 * 15 * 14)))
33
- return one_generic_energy_to_chopper_parameters(calculate, time, order, parameters)
37
+ return one_generic_energy_to_chopper_parameters(calculate, choppers, time, order, parameters)
34
38
 
35
39
 
36
40
  def cspec_translate_energy_to_chopper_parameters(parameters: dict):
37
41
  from itertools import product
38
42
  from .cspec_choppers import calculate
39
- for name in product(('bw1', 'bw2', 'bw3', 's', 'p', 'm1', 'm2'), ('speed', 'phase')):
43
+ choppers = ('bw1', 'bw2', 'bw3', 's', 'p', 'm1', 'm2')
44
+ for name in product(choppers, ('speed', 'phase')):
40
45
  name = ''.join(name)
41
46
  if name not in parameters:
42
47
  parameters[name] = 0
43
48
  time = get_and_remove(parameters, 'time', 0.004)
44
49
  order = get_and_remove(parameters, 'order', 16)
45
- return one_generic_energy_to_chopper_parameters(calculate, time, order, parameters)
50
+ return one_generic_energy_to_chopper_parameters(calculate, choppers, time, order, parameters)
46
51
 
47
52
 
48
53
  def no_op_translate_energy_to_chopper_parameters(parameters: dict):
@@ -1,6 +1,8 @@
1
1
  """
2
2
  Utilities for interfacing with mccode_antlr.instr.Instr objects
3
3
  """
4
+ from __future__ import annotations
5
+
4
6
  from pathlib import Path
5
7
  from typing import Union
6
8
  from mccode_antlr.instr import Instr
@@ -8,8 +10,8 @@ from mccode_antlr.instr import Instr
8
10
 
9
11
  def load_instr(filepath: Union[str, Path]) -> Instr:
10
12
  """Loads an Instr object from a .instr file or a HDF5 file"""
11
- from mccode_antlr.loader import load_mcstas_instr
12
13
  from mccode_antlr.io import load_hdf5
14
+ from mccode_antlr.loader import load_mcstas_instr
13
15
 
14
16
  if not isinstance(filepath, Path):
15
17
  filepath = Path(filepath)
@@ -44,7 +46,7 @@ def collect_parameter_dict(instr: Instr, kwargs: dict, strict: bool = True) -> d
44
46
  for k, v in kwargs.items():
45
47
  if k not in parameters:
46
48
  if strict:
47
- raise ValueError(f"Parameter {k} is not a valid parameter name")
49
+ raise ValueError(f"Parameter {k} is not a valid parameter name. Valid names are: {', '.join(parameters)}")
48
50
  continue
49
51
  if not isinstance(v, Value):
50
52
  expected_type = parameters[k].data_type
@@ -1,11 +1,11 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import Union
4
3
  from pathlib import Path
5
- from .range import Singular, MRange
6
4
  from .tables import SimulationEntry, InstrEntry
7
- from mccode_antlr.compiler.c import CBinaryTarget
8
5
 
6
+ def mcpl_parameter_split(s: str) -> tuple[str, str]:
7
+ k, v = s.split(':', 1)
8
+ return k, v
9
9
 
10
10
  def make_splitrun_parser():
11
11
  from argparse import ArgumentParser
@@ -40,6 +40,12 @@ def make_splitrun_parser():
40
40
  # splitrun controlling parameters
41
41
  aa('--split-at', nargs=1, type=str, default=['mcpl_split'],
42
42
  help='Component at which to split -- DEFAULT: mcpl_split')
43
+ aa('--mcpl-output-component', nargs=1, type=str, default=None,
44
+ help='Inserted MCPL file producing component, MCPL_output(.comp) if not provided')
45
+ aa('--mcpl-input-component', nargs=1, type=str, default=None,
46
+ help='Inserted MCPL file consuming component, MCPL_input(.comp) if not provided')
47
+ aa('--mcpl-input-parameters', nargs='+', type=mcpl_parameter_split, metavar='key:value')
48
+ aa('--mcpl-output-parameters', nargs='+', type=mcpl_parameter_split, metavar='key:value')
43
49
  aa('-P', action='append', default=[], help='Cache parameter matching precision')
44
50
 
45
51
  # Other McCode runtime arguments exist, but are likely not used during a scan:
@@ -78,31 +84,6 @@ def regular_mccode_runtime_dict(args: dict) -> dict:
78
84
  return t
79
85
 
80
86
 
81
- def mccode_runtime_dict_to_args_list(args: dict) -> list[str]:
82
- """Convert a dictionary of McCode runtime arguments to a string.
83
-
84
- :parameter args: A dictionary of McCode runtime arguments.
85
- :return: A list of arguments suitable for use in a command line call to a McCode compiled instrument.
86
- """
87
- # convert to a standardized string:
88
- out = []
89
- if 'seed' in args and args['seed'] is not None:
90
- out.append(f'--seed={args["seed"]}')
91
- if 'ncount' in args and args['ncount'] is not None:
92
- out.append(f'--ncount={args["ncount"]}')
93
- if 'dir' in args and args['dir'] is not None:
94
- out.append(f'--dir={args["dir"]}')
95
- if 'trace' in args and args['trace']:
96
- out.append('--trace')
97
- if 'gravitation' in args and args['gravitation']:
98
- out.append('--gravitation')
99
- if 'bufsiz' in args and args['bufsiz'] is not None:
100
- out.append(f'--bufsiz={args["bufsiz"]}')
101
- if 'format' in args and args['format'] is not None:
102
- out.append(f'--format={args["format"]}')
103
- return out
104
-
105
-
106
87
  def parse_splitrun_precision(unparsed: list[str]) -> dict[str, float]:
107
88
  precision = {}
108
89
  for p in unparsed:
@@ -113,43 +94,34 @@ def parse_splitrun_precision(unparsed: list[str]) -> dict[str, float]:
113
94
  return precision
114
95
 
115
96
 
116
- def sort_args(args: list[str]) -> list[str]:
117
- """Take the list of arguments and sort them into the correct order for splitrun"""
118
- # TODO this is a bit of a hack, but it works for now
119
- first, last = [], []
120
- k = 0
121
- while k < len(args):
122
- if args[k].startswith('-'):
123
- first.append(args[k])
124
- k += 1
125
- if '=' not in first[-1] and k < len(args) and not args[k].startswith('-') and '=' not in args[k]:
126
- first.append(args[k])
127
- k += 1
128
- else:
129
- last.append(args[k])
130
- k += 1
131
- return first + last
132
-
133
-
134
- def parse_splitrun():
97
+ def parse_splitrun(parser):
135
98
  from .range import parse_scan_parameters
99
+ from mccode_antlr.run.runner import sort_args
136
100
  import sys
137
101
  sys.argv[1:] = sort_args(sys.argv[1:])
138
102
 
139
- args = make_splitrun_parser().parse_args()
103
+ args = parser.parse_args()
104
+ if args.mcpl_input_parameters is not None:
105
+ args.mcpl_input_parameters = dict(args.mcpl_input_parameters)
106
+ if args.mcpl_output_parameters is not None:
107
+ args.mcpl_output_parameters = dict(args.mcpl_output_parameters)
140
108
  parameters = parse_scan_parameters(args.parameters)
141
109
  precision = parse_splitrun_precision(args.P)
142
110
  return args, parameters, precision
143
111
 
144
112
 
145
113
  def entrypoint():
146
- args, parameters, precision = parse_splitrun()
114
+ args, parameters, precision = parse_splitrun(make_splitrun_parser())
147
115
  splitrun_from_file(args, parameters, precision)
148
116
 
149
117
 
150
118
  def splitrun_from_file(args, parameters, precision):
151
119
  from .instr import load_instr
152
120
  instr = load_instr(args.instrument[0])
121
+ splitrun_args(instr, parameters, precision, args)
122
+
123
+
124
+ def splitrun_args(instr, parameters, precision, args, **kwargs):
153
125
  splitrun(instr, parameters, precision, split_at=args.split_at[0], grid=args.mesh,
154
126
  seed=args.seed[0] if args.seed is not None else None,
155
127
  ncount=args.ncount[0] if args.ncount is not None else None,
@@ -164,6 +136,11 @@ def splitrun_from_file(args, parameters, precision):
164
136
  parallel=args.parallel,
165
137
  gpu=args.gpu,
166
138
  process_count=args.process_count,
139
+ mcpl_output_component=args.mcpl_output_component[0] if args.mcpl_output_component is not None else None,
140
+ mcpl_output_parameters=args.mcpl_output_parameters,
141
+ mcpl_input_component=args.mcpl_input_component[0] if args.mcpl_input_component is not None else None,
142
+ mcpl_input_parameters=args.mcpl_input_parameters,
143
+ **kwargs
167
144
  )
168
145
 
169
146
 
@@ -174,8 +151,11 @@ def splitrun(instr, parameters, precision: dict[str, float], split_at=None, grid
174
151
  parallel=False, gpu=False, process_count=0,
175
152
  callback=None, callback_arguments: dict[str, str] | None = None,
176
153
  output_split_instrs=True,
154
+ mcpl_output_component=None, mcpl_output_parameters: dict[str, str] | None = None,
155
+ mcpl_input_component=None, mcpl_input_parameters: dict[str, str] | None = None,
177
156
  **runtime_arguments):
178
157
  from zenlog import log
158
+ from mccode_antlr.common import ComponentParameter, Expr
179
159
  from .energy import get_energy_parameter_names
180
160
  if split_at is None:
181
161
  split_at = 'mcpl_split'
@@ -183,7 +163,17 @@ def splitrun(instr, parameters, precision: dict[str, float], split_at=None, grid
183
163
  if not instr.has_component_named(split_at):
184
164
  log.error(f'The specified split-at component, {split_at}, does not exist in the instrument file')
185
165
  # splitting defines an instrument parameter in both returned instrument, 'mcpl_filename'.
186
- pre, post = instr.mcpl_split(split_at, remove_unused_parameters=True)
166
+ if mcpl_output_parameters is not None:
167
+ mcpl_output_parameters = tuple(ComponentParameter(k, Expr.parse(v)) for k, v in mcpl_output_parameters.items())
168
+ if mcpl_input_parameters is not None:
169
+ mcpl_input_parameters = tuple(ComponentParameter(k, Expr.parse(v)) for k, v in mcpl_input_parameters.items())
170
+ pre, post = instr.mcpl_split(split_at,
171
+ output_component=mcpl_output_component,
172
+ output_parameters=mcpl_output_parameters,
173
+ input_component=mcpl_input_component,
174
+ input_parameters=mcpl_input_parameters,
175
+ remove_unused_parameters=True
176
+ )
187
177
  if output_split_instrs:
188
178
  for p in (pre, post):
189
179
  with open(f'{p.name}.instr', 'w') as f:
@@ -204,7 +194,8 @@ def splitrun(instr, parameters, precision: dict[str, float], split_at=None, grid
204
194
  dry_run=dry_run, parallel=parallel, gpu=gpu, process_count=process_count)
205
195
 
206
196
  splitrun_combined(pre_entry, pre, post, pre_parameters, post_parameters, grid, precision,
207
- dry_run=dry_run, callback=callback, callback_arguments=callback_arguments, **runtime_arguments)
197
+ dry_run=dry_run, parallel=parallel, gpu=gpu, process_count=process_count,
198
+ callback=callback, callback_arguments=callback_arguments, **runtime_arguments)
208
199
 
209
200
 
210
201
  def splitrun_pre(instr, parameters, grid, precision: dict[str, float],
@@ -259,6 +250,7 @@ def _pre_step(instr, entry, names, precision, translate, kw, min_pc, max_pc, dry
259
250
 
260
251
  def splitrun_combined(pre_entry, pre, post, pre_parameters, post_parameters, grid, precision: dict[str, float],
261
252
  summary=True, dry_run=False, callback=None, callback_arguments: dict[str, str] | None = None,
253
+ parallel=False, gpu=False, process_count=0,
262
254
  **runtime_arguments):
263
255
  from pathlib import Path
264
256
  from .cache import cache_instr, cache_get_simulation
@@ -267,7 +259,7 @@ def splitrun_combined(pre_entry, pre, post, pre_parameters, post_parameters, gri
267
259
  from .instr import collect_parameter_dict
268
260
  from .tables import best_simulation_entry_match
269
261
  from .emulate import mccode_sim_io, mccode_dat_io, mccode_dat_line
270
- instr_entry = cache_instr(post, mpi=False, acc=False)
262
+ instr_entry = cache_instr(post, mpi=parallel, acc=gpu)
271
263
  args = regular_mccode_runtime_dict(runtime_arguments)
272
264
  sit_kw = {'seed': args.get('seed'), 'ncount': args.get('ncount'), 'gravitation': args.get('gravitation', False)}
273
265
  # recombine the parameters to ensure the 'correct' scan is performed
@@ -308,7 +300,7 @@ def splitrun_combined(pre_entry, pre, post, pre_parameters, post_parameters, gri
308
300
  # TODO Use the following line instead of the one after it when McCode is fixed to use zero-padded folder names
309
301
  # # runtime_arguments['dir'] = args["dir"].joinpath(str(number).zfill(n_zeros))
310
302
  runtime_arguments['dir'] = args['dir'].joinpath(str(number))
311
- do_secondary_simulation(sim_entry, instr_entry, secondary_pars, runtime_arguments, dry_run=dry_run)
303
+ do_secondary_simulation(sim_entry, instr_entry, secondary_pars, runtime_arguments, dry_run=dry_run, process_count=process_count)
312
304
  if summary and not dry_run:
313
305
  # the data file has *all* **scanned** parameters recorded for each step:
314
306
  detectors, line = mccode_dat_line(runtime_arguments['dir'], {k: v for k,v in zip(names, values)})
@@ -332,7 +324,8 @@ def splitrun_combined(pre_entry, pre, post, pre_parameters, post_parameters, gri
332
324
 
333
325
 
334
326
  def _args_pars_mcpl(args: dict, params: dict, mcpl_filename) -> str:
335
- # Combine the arguments, parameters, and mcpl filename into a single command-arguments string:
327
+ """Combine the arguments, parameters, and mcpl filename into a single command-arguments string:"""
328
+ from mccode_antlr.run.runner import mccode_runtime_dict_to_args_list
336
329
  first = ' '.join(mccode_runtime_dict_to_args_list(args))
337
330
  second = ' '.join([f'{k}={v}' for k, v in params.items()])
338
331
  third = f'mcpl_filename={mcpl_filename}'
@@ -457,7 +450,8 @@ def repeat_simulation_until(count, runner, args: dict, parameters, work_dir: Pat
457
450
  combine_mccode_sims_in_directories(outputs, work_dir)
458
451
 
459
452
 
460
- def do_secondary_simulation(p_sit: SimulationEntry, entry: InstrEntry, pars: dict, args: dict, dry_run: bool = False):
453
+ def do_secondary_simulation(p_sit: SimulationEntry, entry: InstrEntry, pars: dict, args: dict, dry_run: bool = False,
454
+ process_count: int = 0):
461
455
  from zenlog import log
462
456
  from pathlib import Path
463
457
  from shutil import copy
@@ -475,7 +469,7 @@ def do_secondary_simulation(p_sit: SimulationEntry, entry: InstrEntry, pars: dic
475
469
 
476
470
  mcpl_path = mcpl_real_filename(Path(p_sit.output_path).joinpath(mcpl_filename))
477
471
  executable = Path(entry.binary_path)
478
- target = CBinaryTarget(mpi=entry.mpi, acc=entry.acc, count=1 if not entry.mpi else 0, nexus=False)
472
+ target = CBinaryTarget(mpi=entry.mpi, acc=entry.acc, count=process_count, nexus=False)
479
473
  run_compiled_instrument(executable, target, _args_pars_mcpl(args, pars, mcpl_path), capture=False, dry_run=dry_run)
480
474
 
481
475
  if not dry_run:
@@ -10,8 +10,8 @@ def uuid():
10
10
 
11
11
 
12
12
  def utc_timestamp() -> float:
13
- from datetime import datetime
14
- return datetime.utcnow().timestamp()
13
+ from datetime import datetime, timezone
14
+ return datetime.now(timezone.utc).timestamp()
15
15
 
16
16
 
17
17
  COMMON_COLUMNS = ['seed', 'ncount', 'output_path', 'gravitation', 'creation', 'last_access']
@@ -1,14 +1,22 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: restage
3
- Version: 0.3.2
3
+ Version: 0.4.1
4
4
  Author-email: Gregory Tucker <gregory.tucker@ess.eu>
5
+ License: BSD-3-Clause
5
6
  Classifier: License :: OSI Approved :: BSD License
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Programming Language :: Python :: 3 :: Only
9
+ Classifier: Programming Language :: Python :: 3.9
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Requires-Python: >=3.9
6
15
  Description-Content-Type: text/markdown
7
16
  Requires-Dist: zenlog>=1.1
8
17
  Requires-Dist: platformdirs>=3.11
9
18
  Requires-Dist: psutil>=5.9.6
10
- Requires-Dist: importlib_metadata; python_version < "3.8"
11
- Requires-Dist: mccode-antlr[hdf5]>=0.7.1
19
+ Requires-Dist: mccode-antlr[hdf5]>=0.10.2
12
20
  Provides-Extra: test
13
21
  Requires-Dist: pytest; extra == "test"
14
22
  Requires-Dist: chopcal; extra == "test"
@@ -23,8 +23,6 @@ src/restage.egg-info/dependency_links.txt
23
23
  src/restage.egg-info/entry_points.txt
24
24
  src/restage.egg-info/requires.txt
25
25
  src/restage.egg-info/top_level.txt
26
- test/splitRunTest_first.c
27
- test/splitRunTest_second.c
28
26
  test/test_cache.py
29
27
  test/test_database.py
30
28
  test/test_energy.py
@@ -0,0 +1,8 @@
1
+ zenlog>=1.1
2
+ platformdirs>=3.11
3
+ psutil>=5.9.6
4
+ mccode-antlr[hdf5]>=0.10.2
5
+
6
+ [test]
7
+ pytest
8
+ chopcal
@@ -1,20 +1,31 @@
1
1
  import unittest
2
2
 
3
+
4
+ def parameters(names: tuple[str, ...]):
5
+ from itertools import product
6
+ return tuple(x + y for x, y in product(names, ('speed', 'phase')))
7
+
8
+
9
+ CHOPPER_NAMES = ('pulse_shaping', 'frame_overlap', 'bandwidth')
10
+ CHOPPERS = tuple(f'{name}_chopper_{no}' for name in CHOPPER_NAMES for no in (1, 2))
11
+ OLD_CHOPPERS = tuple(f'{name}{no}' for name in ('ps', 'fo', 'bw') for no in (1, 2))
12
+
13
+
3
14
  class BIFROSTEnergyTestCase(unittest.TestCase):
4
15
  def setUp(self):
5
16
  from mccode_antlr.loader import parse_mcstas_instr
6
17
  instr = f"""DEFINE INSTRUMENT this_IS_NOT_BIFROST(
7
- ps1speed, ps1phase, ps2speed, ps2phase, fo1speed, fo1phase, bw1speed, bw1phase, bw2speed, bw2phase
18
+ pulse_shaping_chopper_1speed, pulse_shaping_chopper_1phase, pulse_shaping_chopper_2speed, pulse_shaping_chopper_2phase, frame_overlap_chopper_1speed, frame_overlap_chopper_1phase, bandwidth_chopper_1speed, bandwidth_chopper_1phase, bandwidth_chopper_2speed, bandwidth_chopper_2phase
8
19
  )
9
20
  TRACE
10
21
  COMPONENT origin = Arm() AT (0, 0, 0) ABSOLUTE
11
- COMPONENT ps1 = DiskChopper(theta_0=170, radius=0.35, nu=ps1speed, phase=ps1phase) AT (0, 0, 1) RELATIVE PREVIOUS
12
- COMPONENT ps2 = DiskChopper(theta_0=170, radius=0.35, nu=ps2speed, phase=ps2phase) AT (0, 0, 0.02) RELATIVE ps1
13
- COMPONENT fo1 = DiskChopper(theta_0=110, radius=0.35, nu=fo1speed, phase=fo1phase) AT (0, 0, 12) RELATIVE ps2
14
- COMPONENT fo2 = DiskChopper(theta_0=115, radius=0.35, nu=fo1speed, phase=fo1phase) AT (0, 0, 4) RELATIVE fo1
15
- COMPONENT bw1 = DiskChopper(theta_0=110, radius=0.35, nu=bw1speed, phase=bw1phase) AT (0, 0, 80) RELATIVE fo2
16
- COMPONENT bw2 = DiskChopper(theta_0=115, radius=0.35, nu=bw2speed, phase=bw2phase) AT (0, 0, 0.02) RELATIVE bw1
17
- COMPONENT sample = Arm() AT (0, 0, 80) RELATIVE bw2
22
+ COMPONENT pulse_shaping_chopper_1 = DiskChopper(theta_0=170, radius=0.35, nu=pulse_shaping_chopper_1speed, phase=pulse_shaping_chopper_1phase) AT (0, 0, 1) RELATIVE PREVIOUS
23
+ COMPONENT pulse_shaping_chopper_2 = DiskChopper(theta_0=170, radius=0.35, nu=pulse_shaping_chopper_2speed, phase=pulse_shaping_chopper_2phase) AT (0, 0, 0.02) RELATIVE pulse_shaping_chopper_1
24
+ COMPONENT frame_overlap_chopper_1 = DiskChopper(theta_0=110, radius=0.35, nu=frame_overlap_chopper_1speed, phase=frame_overlap_chopper_1phase) AT (0, 0, 12) RELATIVE pulse_shaping_chopper_2
25
+ COMPONENT frame_overlap_chopper_2 = DiskChopper(theta_0=115, radius=0.35, nu=frame_overlap_chopper_1speed, phase=frame_overlap_chopper_1phase) AT (0, 0, 4) RELATIVE frame_overlap_chopper_1
26
+ COMPONENT bandwidth_chopper_1 = DiskChopper(theta_0=110, radius=0.35, nu=bandwidth_chopper_1speed, phase=bandwidth_chopper_1phase) AT (0, 0, 80) RELATIVE frame_overlap_chopper_2
27
+ COMPONENT bandwidth_chopper_2 = DiskChopper(theta_0=115, radius=0.35, nu=bandwidth_chopper_2speed, phase=bandwidth_chopper_2phase) AT (0, 0, 0.02) RELATIVE bandwidth_chopper_1
28
+ COMPONENT sample = Arm() AT (0, 0, 80) RELATIVE bandwidth_chopper_2
18
29
  END
19
30
  """
20
31
  self.instr = parse_mcstas_instr(instr)
@@ -46,8 +57,8 @@ class BIFROSTEnergyTestCase(unittest.TestCase):
46
57
  e = y * 0.5 + 1.7
47
58
  self.assertAlmostEqual(x, e)
48
59
 
49
- parameters = dict(order=order, time=time, ei=ei)
50
- npts, names, points = parameters_to_scan(parameters, grid=True)
60
+ scan_parameters = dict(order=order, time=time, ei=ei)
61
+ npts, names, points = parameters_to_scan(scan_parameters, grid=True)
51
62
  self.assertEqual(npts, 47*11)
52
63
  self.assertEqual(names, ['order', 'time', 'ei'])
53
64
  all_points = list(points)
@@ -68,7 +79,7 @@ class BIFROSTEnergyTestCase(unittest.TestCase):
68
79
  from restage.energy import energy_to_chopper_translator
69
80
  from restage.energy import bifrost_translate_energy_to_chopper_parameters
70
81
  from restage.range import MRange, Singular, parameters_to_scan
71
- from itertools import product
82
+
72
83
 
73
84
  translator = energy_to_chopper_translator(self.instr.name)
74
85
  self.assertEqual(translator, bifrost_translate_energy_to_chopper_parameters)
@@ -76,36 +87,33 @@ class BIFROSTEnergyTestCase(unittest.TestCase):
76
87
  order = Singular(14, 1)
77
88
  time = MRange(0.0001, 0.002248, 0.0002)
78
89
  ei = MRange(1.7, 24.7, 0.5)
79
- parameters = dict(order=order, time=time, ei=ei)
90
+ scan_parameters = dict(order=order, time=time, ei=ei)
80
91
 
81
- spts, names, points = parameters_to_scan(parameters, grid=True)
92
+ spts, names, points = parameters_to_scan(scan_parameters, grid=True)
82
93
 
83
94
  self.assertEqual(47*11, spts)
84
95
  self.assertEqual(names, ['order', 'time', 'ei'])
85
96
 
86
- chopper_pars = [x + y for x, y in product(('ps1', 'ps2', 'fo1', 'fo2', 'bw1', 'bw2'), ('speed', 'phase'))]
87
-
97
+ chopper_parameters = parameters(CHOPPERS)
88
98
  for point in points:
89
99
  kv = {k: v for k, v in zip(names, point)}
90
100
  translated = translator(kv)
91
- for x in chopper_pars:
92
- self.assertTrue(''.join(x) in translated)
101
+ for x in chopper_parameters:
102
+ self.assertTrue(x in translated)
93
103
 
94
- self.assertEqual(len(translated), len(chopper_pars))
95
- self.assertAlmostEqual(translated['bw1speed'], 14.0)
96
- self.assertAlmostEqual(translated['bw2speed'], -14.0)
97
- self.assertAlmostEqual(translated['fo1speed'], 14.0)
98
- self.assertAlmostEqual(translated['fo1speed'], 14.0)
99
- self.assertAlmostEqual(translated['ps1speed'], 14*14.0)
100
- self.assertAlmostEqual(translated['ps2speed'], 14*14.0)
104
+ self.assertEqual(len(translated), len(chopper_parameters))
105
+ self.assertAlmostEqual(translated['bandwidth_chopper_1speed'], 14.0)
106
+ self.assertAlmostEqual(translated['bandwidth_chopper_2speed'], -14.0)
107
+ self.assertAlmostEqual(translated['frame_overlap_chopper_1speed'], 14.0)
108
+ self.assertAlmostEqual(translated['frame_overlap_chopper_1speed'], 14.0)
109
+ self.assertAlmostEqual(translated['pulse_shaping_chopper_1speed'], 14*14.0)
110
+ self.assertAlmostEqual(translated['pulse_shaping_chopper_2speed'], 14*14.0)
101
111
 
102
112
  def test_calculations(self):
103
113
  from itertools import product
104
114
  from chopcal import bifrost as mcstas_bifrost_calculation
105
115
  from restage.energy import bifrost_translate_energy_to_chopper_parameters
106
116
 
107
- pars = [x+y for x, y in product(('ps1', 'ps2', 'fo1', 'fo2', 'bw1', 'bw2'), ('speed', 'phase'))]
108
-
109
117
  shortest_time = 0.0001 # this is approximately twice the opening time of the pulse shaping choppers at 15*14 Hz
110
118
  # Normal operation Shortest full-height pulse Shorter pulses reduce height
111
119
  # /-----\ /\
@@ -129,8 +137,8 @@ class BIFROSTEnergyTestCase(unittest.TestCase):
129
137
  kv = {'order': order, 'time': time, 'ei': energy}
130
138
  translated = bifrost_translate_energy_to_chopper_parameters(kv)
131
139
  from_mcstas = mcstas_bifrost_calculation(energy, 0., time)
132
- for x in pars:
133
- self.assertAlmostEqual(from_mcstas[x], translated[x])
140
+ for o, x in zip(parameters(OLD_CHOPPERS), parameters(CHOPPERS)):
141
+ self.assertAlmostEqual(from_mcstas[o], translated[x])
134
142
 
135
143
 
136
144
  if __name__ == '__main__':