casm-tools 2.0a1__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.
@@ -0,0 +1 @@
1
+ """The casm-calc program"""
@@ -0,0 +1 @@
1
+ """The casm-calc commands"""
@@ -0,0 +1,97 @@
1
+ """Implements ``casm-calc ...``"""
2
+
3
+ import argparse
4
+ import os
5
+ import sys
6
+
7
+
8
+ def make_parser():
9
+ """Make the ``casm-calc ...`` argument parser
10
+
11
+ Returns
12
+ -------
13
+ parser: argparse.ArgumentParser
14
+ The argument parser for the `casm-map` program.
15
+
16
+ """
17
+
18
+ from .status import make_status_subparser
19
+ from .submit import make_submit_subparser
20
+ from .vasp import make_vasp_subparser
21
+
22
+ ### casm-calc ...
23
+ parser = argparse.ArgumentParser(
24
+ description="CASM calculation CLI tool",
25
+ )
26
+ c = parser.add_subparsers(title="Select which calculator to use")
27
+
28
+ ### casm-calc ...
29
+ make_vasp_subparser(c)
30
+ make_status_subparser(c)
31
+ make_submit_subparser(c)
32
+
33
+ return parser
34
+
35
+
36
+ def main(argv=None, working_dir=None):
37
+ """Implements ``casm-calc ...``
38
+
39
+ Parameters
40
+ ----------
41
+ argv : list of str, optional
42
+ The command line arguments to parse. If None, uses `sys.argv`.
43
+ working_dir : str, optional
44
+ The working directory to use. If None, uses the current working directory.
45
+
46
+ Returns
47
+ -------
48
+ code: int
49
+ A return code indicating success (0) or failure (non-zero).
50
+
51
+ """
52
+
53
+ from casm.tools.shared import contexts
54
+
55
+ if argv is None:
56
+ argv = sys.argv
57
+ if working_dir is None:
58
+ working_dir = os.getcwd()
59
+
60
+ parser = make_parser()
61
+
62
+ # if "--desc" is in the arguments, print the description:
63
+ if "--desc" in argv:
64
+
65
+ if "vasp" in argv:
66
+ from .vasp import print_desc
67
+
68
+ print_desc(argv=argv)
69
+ return 0
70
+ elif "status" in argv:
71
+ from .submit import print_desc
72
+
73
+ print_desc(argv=argv)
74
+ return 0
75
+
76
+ elif "submit" in argv:
77
+
78
+ from .submit import print_desc
79
+
80
+ print_desc(argv=argv)
81
+
82
+ return 0
83
+
84
+ else:
85
+ parser.print_help()
86
+ return 1
87
+
88
+ if len(argv) < 2:
89
+ parser.print_help()
90
+ return 1
91
+ args = parser.parse_args(argv[1:])
92
+
93
+ code = 0
94
+ with contexts.working_dir(working_dir):
95
+ code = args.func(args)
96
+
97
+ return code
@@ -0,0 +1,428 @@
1
+ """Implements ``casm-calc status ...``"""
2
+
3
+ import importlib.util
4
+ import sys
5
+ import typing
6
+
7
+ # Note:
8
+ # The command `casm-calc status` is implemented by `run_status` which requires
9
+ # `casm.project` to be installed. This is a bit odd, because `casm.tools` is a
10
+ # dependency of `casm.project`. At some point `casm-calc status` will be refactored
11
+ # to be a plugin provided by `casm.project`.
12
+
13
+ if typing.TYPE_CHECKING:
14
+ import argparse
15
+
16
+ from casm.project import ConfigSelection, Project
17
+
18
+
19
+ def _validate_casm_project():
20
+ if importlib.util.find_spec("casm.project") is None:
21
+ print(
22
+ """
23
+ casm.project is not installed. To use `casm-calc status` install casm-project with:
24
+
25
+ pip install casm-project"""
26
+ )
27
+ sys.exit(1)
28
+
29
+
30
+ def _print_available(
31
+ items: list[str],
32
+ item_type_desc: str,
33
+ default_item: typing.Optional[str] = None,
34
+ ):
35
+ """Prints available items of a given type."""
36
+ print(f"Available {item_type_desc}:")
37
+ for item in items:
38
+ suffix = ""
39
+ if default_item is not None and item == default_item:
40
+ suffix = " (default)"
41
+ print(f"- {item}{suffix}")
42
+ print()
43
+
44
+
45
+ def make_config_selection(
46
+ project: "Project",
47
+ args: "argparse.Namespace",
48
+ ) -> "ConfigSelection":
49
+ """Constructs a configuration selection based on the provided arguments.
50
+
51
+ Parameters
52
+ ----------
53
+ project: Project
54
+ The CASM project to work with.
55
+ args: argparse.Namespace
56
+ The parsed arguments from the command line. Uses:
57
+ - `args.enum`: str, Enumeration ID
58
+ - `args.calctype`: str, A calctype ID to specify the calculation type.
59
+ - `args.clex`: str, Cluster expansion ID to specify the calctype. This is the
60
+ fallback, used if `args.calctype` is not specified. If this is also not
61
+ specified, the default cluster expansion is used.
62
+ - `args.selection`: str, optional, Name of a selection in the enumeration
63
+ to select configurations from.
64
+
65
+
66
+ Returns
67
+ -------
68
+ config_selection: Union[casm.project.enum.ConfigSelection, int]
69
+ A ConfigSelection, or a return code indicating failure (non-zero).
70
+
71
+ """
72
+
73
+ # --- Variables ---
74
+ enum_id = args.enum
75
+ clex_id = args.clex
76
+ calc_id = args.calctype
77
+ selection_name = args.selection
78
+
79
+ # --- Construct enum ---
80
+ if enum_id not in project.enum.all():
81
+ print(f"Enumeration ID '{enum_id}' not found in project.")
82
+ _print_available(
83
+ items=project.enum.all(),
84
+ item_type_desc="enumerations",
85
+ )
86
+ return 1
87
+ enum = project.enum.get(enum_id)
88
+
89
+ # --- Construct config_selection ---
90
+ if calc_id is not None:
91
+ if calc_id not in project.calc.all():
92
+ print(f"Calculation type '{calc_id}' not found in project.")
93
+ _print_available(
94
+ items=project.calc.all(),
95
+ item_type_desc="calculation types",
96
+ default_item=project.settings.default_clex.calctype,
97
+ )
98
+ return 1
99
+ clex = project.settings.default_clex
100
+ clex.calctype = calc_id
101
+
102
+ elif clex_id is not None:
103
+ if clex_id not in project.settings.cluster_expansions:
104
+ print(f"Cluster expansion '{clex_id}' not found in project.")
105
+ _print_available(
106
+ items=project.settings.cluster_expansions.keys(),
107
+ item_type_desc="cluster expansions",
108
+ default_item=project.settings.default_clex_name,
109
+ )
110
+ return 1
111
+ clex = project.settings.get_clex(name=clex_id)
112
+ else:
113
+ clex = project.settings.default_clex
114
+
115
+ all_selections = enum.all_config_selections()
116
+ if selection_name is None:
117
+ i = 0
118
+ selection_name = f"all-{i}"
119
+ while selection_name in all_selections:
120
+ i += 1
121
+ selection_name = f"all-{i}"
122
+ elif selection_name not in all_selections:
123
+ print(f"Selection '{selection_name}' not found in enumeration '{enum_id}'.")
124
+ _print_available(
125
+ items=all_selections,
126
+ item_type_desc="selections",
127
+ )
128
+ return 1
129
+
130
+ # --- Select configurations ---
131
+ config_selection = enum.config_selection(
132
+ name=selection_name,
133
+ clex=clex,
134
+ )
135
+ print(config_selection)
136
+ print()
137
+ return config_selection
138
+
139
+
140
+ def list_options(
141
+ project: "Project",
142
+ args: "argparse.Namespace",
143
+ ):
144
+ """List available options for enumeration, calctype, clex, and selection."""
145
+ print("Available enumerations:")
146
+ for enum_id in project.enum.all():
147
+ print(f"- {enum_id}")
148
+
149
+ default_clex_name = project.settings.default_clex_name
150
+ default_clex = project.settings.default_clex
151
+
152
+ print("\nAvailable cluster expansions (--clex):")
153
+ for clex_name, clex_desc in project.settings.cluster_expansions.items():
154
+ is_default = ""
155
+ if clex_name == default_clex_name:
156
+ is_default = " (default)"
157
+ print(f"- {clex_name} (calctype={clex_desc.calctype}){is_default}")
158
+
159
+ print("\nAvailable calculation types (--calctype):")
160
+ for calc_id in project.calc.all():
161
+ is_default = ""
162
+ if default_clex is not None:
163
+ if calc_id == default_clex.calctype:
164
+ is_default = " (default)"
165
+ print(f"- {calc_id}{is_default}")
166
+
167
+ if args.enum is not None and args.enum in project.enum.all():
168
+ enum = project.enum.get(args.enum)
169
+ print(f"\nAvailable selections (--selection) for --enum={args.enum}:")
170
+ for selection_name in enum.all_config_selections():
171
+ print(f"- {selection_name}")
172
+
173
+
174
+ def run_status(args):
175
+ """Implements ``casm-calc status ...``
176
+
177
+ Parameters
178
+ ----------
179
+ args : argparse.Namespace
180
+ The parsed arguments from the command line. Uses:
181
+
182
+ - `args.enum`: str, Enumeration ID
183
+ - `args.clex`: str, Cluster expansion ID to specify the calctype
184
+ - `args.selection`: str, optional, Name of a selection in the enumeration
185
+ to check the status of. Select all configurations if not specified.
186
+ - `args.tabulate`: If set, prints a table of job status counts.
187
+ - `args.none`, `args.setup`, `args.started`, `args.stopped`, `args.complete`,
188
+ `args.other`: If set, prints configurations with the corresponding status. If
189
+ multiple are set, prints configurations with any of the specified statuses.
190
+ - `args.all`: If set, checks the status of all configurations in the
191
+ enumeration, not just the selected ones.
192
+ - `args.details`: If set, prints configurations with any status.
193
+ - `args.show_calc_dir`: If set, includes the path to the calculation directory
194
+ in the output.
195
+
196
+ Returns
197
+ -------
198
+ code: int
199
+ A return code indicating success (0) or failure (non-zero).
200
+
201
+ """
202
+ from tabulate import tabulate
203
+
204
+ _validate_casm_project()
205
+
206
+ from casm.project import Project, project_path
207
+
208
+ # --- Load project ---
209
+ path = project_path()
210
+ if path is None:
211
+ print("No CASM project found.")
212
+ return 1
213
+ project = Project(path=path)
214
+
215
+ # --- list options ---
216
+ if args.list:
217
+ list_options(
218
+ project=project,
219
+ args=args,
220
+ )
221
+ return 0
222
+ elif args.enum is None:
223
+ print("No enumeration ID provided. Use --list to see available options.")
224
+ return 1
225
+
226
+ config_selection = make_config_selection(
227
+ project=project,
228
+ args=args,
229
+ )
230
+ if isinstance(config_selection, int):
231
+ # An error occurred in make_config_selection
232
+ return config_selection
233
+
234
+ status_count = dict()
235
+ details = []
236
+ standard_status = [
237
+ "none",
238
+ "setup",
239
+ "submitted",
240
+ "started",
241
+ "canceled",
242
+ "stopped",
243
+ "complete",
244
+ ]
245
+
246
+ def add_details(record):
247
+ _details = [
248
+ record.name,
249
+ record.calc_status,
250
+ record.calc_jobid,
251
+ record.calc_runtime,
252
+ ]
253
+ details.append(_details)
254
+
255
+ details_header_printed = False
256
+
257
+ for record in config_selection.all:
258
+ if not args.all and not record.is_selected:
259
+ continue
260
+
261
+ status = record.calc_status
262
+ if status in status_count:
263
+ status_count[status] += 1
264
+ else:
265
+ status_count[status] = 1
266
+
267
+ if (
268
+ args.details
269
+ or (args.none and status == "none")
270
+ or (args.setup and status == "setup")
271
+ or (args.started and status == "started")
272
+ or (args.submitted and status == "submitted")
273
+ or (args.canceled and status == "canceled")
274
+ or (args.stopped and status == "stopped")
275
+ or (args.complete and status == "complete")
276
+ or (args.other and status not in standard_status)
277
+ ):
278
+ if not details_header_printed:
279
+ print(f"{'Name':36}{'Status':12}{'Job ID':12}{'Runtime':18}")
280
+ print("-" * 78)
281
+ details_header_printed = True
282
+
283
+ name = record.name
284
+ jobid = record.calc_jobid
285
+ runtime = record.calc_runtime
286
+ print(f"{name:36}{status:12}{jobid:12}{runtime:18}")
287
+
288
+ if args.tabulate:
289
+ table = []
290
+ for status, count in status_count.items():
291
+ table.append([status, count])
292
+ print()
293
+ print(tabulate(table, headers=["Status", "Count"]))
294
+ print()
295
+
296
+ return 0
297
+
298
+
299
+ ################################################################################
300
+
301
+
302
+ def print_desc(argv=None):
303
+ print("No extended description available.")
304
+
305
+
306
+ def make_status_subparser(c):
307
+ """Constructs the ``casm-calc status ...`` argument parser, and attaches the methods
308
+ for running the subcommands.
309
+
310
+ Parameters
311
+ ----------
312
+ c: argparse._SubParsersAction
313
+ The output from ``parser.add_subparsers`` to which ``casm-calc status``
314
+ arguments are added.
315
+
316
+ Returns
317
+ -------
318
+ code: int
319
+ A return code indicating success (0) or failure (non-zero).
320
+
321
+ """
322
+ status = c.add_parser(
323
+ "status",
324
+ help="Chck the status of CASM project calculations",
325
+ description="Check the status of CASM project calculations",
326
+ )
327
+
328
+ ### casm-calc status ....
329
+ status.set_defaults(func=run_status)
330
+
331
+ status.add_argument(
332
+ "enum",
333
+ type=str,
334
+ help=("Enumeration ID"),
335
+ nargs="?",
336
+ )
337
+ status.add_argument(
338
+ "-c",
339
+ "--selection",
340
+ type=str,
341
+ help=(
342
+ "Name of a selection in the enumeration with selected configurations to "
343
+ "check the calculation status. Select all configurations if not specified."
344
+ ),
345
+ )
346
+ status.add_argument(
347
+ "-a",
348
+ "--all",
349
+ action="store_true",
350
+ help=(
351
+ "If given, check the status of all configurations in the enumeration, "
352
+ "regardless of selection. If not given, only check selected configurations."
353
+ ),
354
+ )
355
+ status.add_argument(
356
+ "--calctype",
357
+ type=str,
358
+ help=(
359
+ "A calctype ID, used to indicate which calctype to check "
360
+ "the calculation status. Uses --clex if not specified. "
361
+ ),
362
+ )
363
+ status.add_argument(
364
+ "--clex",
365
+ type=str,
366
+ help=(
367
+ "A cluster expansion key, used to indicate which calctype to check "
368
+ "the calculation status. Uses the default clex if not specified. "
369
+ ),
370
+ )
371
+ status.add_argument(
372
+ "-t",
373
+ "--tabulate",
374
+ action="store_true",
375
+ help=("Print table of job status counts."),
376
+ )
377
+ status.add_argument(
378
+ "-d",
379
+ "--details",
380
+ action="store_true",
381
+ help=("Print configurations with any status."),
382
+ )
383
+ status.add_argument(
384
+ "--none",
385
+ action="store_true",
386
+ help=('Print configurations with status="none".'),
387
+ )
388
+ status.add_argument(
389
+ "--setup",
390
+ action="store_true",
391
+ help=('Print configurations with status="setup".'),
392
+ )
393
+ status.add_argument(
394
+ "--started",
395
+ action="store_true",
396
+ help=('Print configurations with status="started".'),
397
+ )
398
+ status.add_argument(
399
+ "--submitted",
400
+ action="store_true",
401
+ help=('Print configurations with status="submitted".'),
402
+ )
403
+ status.add_argument(
404
+ "--canceled",
405
+ action="store_true",
406
+ help=('Print configurations with status="canceled".'),
407
+ )
408
+ status.add_argument(
409
+ "--stopped",
410
+ action="store_true",
411
+ help=('Print configurations with status="stopped".'),
412
+ )
413
+ status.add_argument(
414
+ "--complete",
415
+ action="store_true",
416
+ help=('Print configurations with status="complete".'),
417
+ )
418
+ status.add_argument(
419
+ "--other",
420
+ action="store_true",
421
+ help=("Print jobs with any other status."),
422
+ )
423
+ status.add_argument(
424
+ "-l",
425
+ "--list",
426
+ action="store_true",
427
+ help=("If given, list enumeration, calctype, clex, and selection options."),
428
+ )