lheutils 0.0.2__tar.gz → 0.0.4__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.
- {lheutils-0.0.2 → lheutils-0.0.4}/PKG-INFO +7 -18
- {lheutils-0.0.2 → lheutils-0.0.4}/README.md +4 -3
- {lheutils-0.0.2 → lheutils-0.0.4}/pyproject.toml +5 -15
- lheutils-0.0.4/src/lheutils/__init__.py +3 -0
- {lheutils-0.0.2 → lheutils-0.0.4}/src/lheutils/_version.py +2 -2
- lheutils-0.0.4/src/lheutils/_version.pyi +2 -0
- {lheutils-0.0.2 → lheutils-0.0.4}/src/lheutils/cli/lhe2lhe.py +2 -2
- {lheutils-0.0.2 → lheutils-0.0.4}/src/lheutils/cli/lhecheck.py +13 -11
- {lheutils-0.0.2 → lheutils-0.0.4}/src/lheutils/cli/lhediff.py +22 -28
- lheutils-0.0.4/src/lheutils/cli/lhefix.py +161 -0
- {lheutils-0.0.2 → lheutils-0.0.4}/src/lheutils/cli/lheinfo.py +155 -104
- {lheutils-0.0.2 → lheutils-0.0.4}/src/lheutils/cli/lhemerge.py +39 -32
- lheutils-0.0.4/src/lheutils/cli/lheshow.py +158 -0
- lheutils-0.0.4/src/lheutils/cli/lhesplit.py +153 -0
- {lheutils-0.0.2 → lheutils-0.0.4}/src/lheutils/cli/util.py +7 -5
- {lheutils-0.0.2 → lheutils-0.0.4}/tests/cli/test_lhe2lhe.py +4 -4
- {lheutils-0.0.2 → lheutils-0.0.4}/tests/cli/test_lhecheck.py +2 -1
- {lheutils-0.0.2 → lheutils-0.0.4}/tests/cli/test_lhediff.py +2 -1
- lheutils-0.0.4/tests/cli/test_lhesplit.py +0 -0
- lheutils-0.0.2/src/lheutils/__init__.py +0 -1
- lheutils-0.0.2/src/lheutils/cli/lheshow.py +0 -118
- lheutils-0.0.2/src/lheutils/cli/lhesplit.py +0 -132
- {lheutils-0.0.2 → lheutils-0.0.4}/.gitignore +0 -0
- {lheutils-0.0.2 → lheutils-0.0.4}/LICENSE +0 -0
- {lheutils-0.0.2 → lheutils-0.0.4}/src/lheutils/cli/lhefilter.py +0 -0
- {lheutils-0.0.2 → lheutils-0.0.4}/src/lheutils/cli/lhestack.py +0 -0
- {lheutils-0.0.2 → lheutils-0.0.4}/src/lheutils/cli/lheunstack.py +0 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lheutils
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.4
|
|
4
4
|
Summary: Various CLI utilities for handling LHE files
|
|
5
5
|
Project-URL: Homepage, https://github.com/APN-Pucky/lheutils
|
|
6
6
|
Project-URL: Source Code, https://github.com/APN-Pucky/lheutils
|
|
7
7
|
Project-URL: Issue Tracker, https://github.com/APN-Pucky/lheutils/issues
|
|
8
8
|
Project-URL: Releases, https://github.com/APN-Pucky/lheutils/releases
|
|
9
|
-
Author-email: Alexander Puck Neuwirth <
|
|
9
|
+
Author-email: Alexander Puck Neuwirth <alexander.neuwirth@unimib.it>
|
|
10
10
|
License: Apache-2.0
|
|
11
11
|
License-File: LICENSE
|
|
12
12
|
Keywords: lhe,physics
|
|
@@ -26,23 +26,11 @@ Classifier: Topic :: Scientific/Engineering
|
|
|
26
26
|
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
27
27
|
Requires-Python: >=3.9
|
|
28
28
|
Requires-Dist: pylhe>=1.0.0
|
|
29
|
+
Requires-Dist: typing-extensions>=4.0.0
|
|
29
30
|
Provides-Extra: develop
|
|
30
31
|
Requires-Dist: pre-commit; extra == 'develop'
|
|
31
32
|
Requires-Dist: pylhe[lint,test]; extra == 'develop'
|
|
32
33
|
Requires-Dist: tbump>=6.7.0; extra == 'develop'
|
|
33
|
-
Provides-Extra: docs
|
|
34
|
-
Requires-Dist: jupyter-sphinx; extra == 'docs'
|
|
35
|
-
Requires-Dist: myst-nb; extra == 'docs'
|
|
36
|
-
Requires-Dist: myst-parser; extra == 'docs'
|
|
37
|
-
Requires-Dist: nbsphinx; extra == 'docs'
|
|
38
|
-
Requires-Dist: pandoc; extra == 'docs'
|
|
39
|
-
Requires-Dist: sphinx; extra == 'docs'
|
|
40
|
-
Requires-Dist: sphinx-autoapi; extra == 'docs'
|
|
41
|
-
Requires-Dist: sphinx-autobuild; extra == 'docs'
|
|
42
|
-
Requires-Dist: sphinx-copybutton; extra == 'docs'
|
|
43
|
-
Requires-Dist: sphinx-math-dollar; extra == 'docs'
|
|
44
|
-
Requires-Dist: sphinx-rtd-theme>=0.5.0; extra == 'docs'
|
|
45
|
-
Requires-Dist: toml; extra == 'docs'
|
|
46
34
|
Provides-Extra: lint
|
|
47
35
|
Requires-Dist: mypy>=1.0.0; extra == 'lint'
|
|
48
36
|
Requires-Dist: ruff; extra == 'lint'
|
|
@@ -54,7 +42,7 @@ Requires-Dist: pytest>=6.0; extra == 'test'
|
|
|
54
42
|
Requires-Dist: scikit-hep-testdata>=0.5.5; extra == 'test'
|
|
55
43
|
Description-Content-Type: text/markdown
|
|
56
44
|
|
|
57
|
-
# lheutils v0.0.
|
|
45
|
+
# lheutils v0.0.4
|
|
58
46
|
|
|
59
47
|
A collection of utilities for working with LHE files.
|
|
60
48
|
|
|
@@ -62,12 +50,13 @@ A collection of utilities for working with LHE files.
|
|
|
62
50
|
|
|
63
51
|
| Program | Description |
|
|
64
52
|
|---------|-------------|
|
|
53
|
+
| `lhe2lhe` | Convert LHE files with different compression and weight format options. |
|
|
65
54
|
| `lhecheck` | Validate LHE files and check momentum conservation. |
|
|
55
|
+
| `lhediff` | Compare two LHE files and report differences. |
|
|
66
56
|
| `lhefilter` | Filter LHE files based on process ID, particle PDG IDs, and event numbers. |
|
|
67
57
|
| `lheinfo` | Display information about LHE files. |
|
|
58
|
+
| `lhemerge` | Merge LHE files with identical initialization sections (inverse of lhesplit). |
|
|
68
59
|
| `lheshow` | Display specific events or init block from LHE files. |
|
|
69
60
|
| `lhesplit` | Split LHE events from input file into multiple output files. |
|
|
70
|
-
| `lhemerge` | Merge LHE files with identical initialization sections (inverse of lhesplit). |
|
|
71
61
|
| `lhestack` | Stack multiple LHE files into a single file. |
|
|
72
62
|
| `lheunstack` | Split a single LHE file by process ID into separate files (inverse of lhestack). |
|
|
73
|
-
| `lhe2lhe` | Convert LHE files with different compression and weight format options. |
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# lheutils v0.0.
|
|
1
|
+
# lheutils v0.0.4
|
|
2
2
|
|
|
3
3
|
A collection of utilities for working with LHE files.
|
|
4
4
|
|
|
@@ -6,12 +6,13 @@ A collection of utilities for working with LHE files.
|
|
|
6
6
|
|
|
7
7
|
| Program | Description |
|
|
8
8
|
|---------|-------------|
|
|
9
|
+
| `lhe2lhe` | Convert LHE files with different compression and weight format options. |
|
|
9
10
|
| `lhecheck` | Validate LHE files and check momentum conservation. |
|
|
11
|
+
| `lhediff` | Compare two LHE files and report differences. |
|
|
10
12
|
| `lhefilter` | Filter LHE files based on process ID, particle PDG IDs, and event numbers. |
|
|
11
13
|
| `lheinfo` | Display information about LHE files. |
|
|
14
|
+
| `lhemerge` | Merge LHE files with identical initialization sections (inverse of lhesplit). |
|
|
12
15
|
| `lheshow` | Display specific events or init block from LHE files. |
|
|
13
16
|
| `lhesplit` | Split LHE events from input file into multiple output files. |
|
|
14
|
-
| `lhemerge` | Merge LHE files with identical initialization sections (inverse of lhesplit). |
|
|
15
17
|
| `lhestack` | Stack multiple LHE files into a single file. |
|
|
16
18
|
| `lheunstack` | Split a single LHE file by process ID into separate files (inverse of lhestack). |
|
|
17
|
-
| `lhe2lhe` | Convert LHE files with different compression and weight format options. |
|
|
@@ -13,7 +13,7 @@ readme = "README.md"
|
|
|
13
13
|
license = { text = "Apache-2.0" } # SPDX short identifier
|
|
14
14
|
requires-python = ">=3.9"
|
|
15
15
|
authors = [
|
|
16
|
-
{ name = "Alexander Puck Neuwirth", email = "
|
|
16
|
+
{ name = "Alexander Puck Neuwirth", email = "alexander.neuwirth@unimib.it" },
|
|
17
17
|
]
|
|
18
18
|
keywords = [
|
|
19
19
|
"lhe",
|
|
@@ -37,6 +37,7 @@ classifiers = [
|
|
|
37
37
|
]
|
|
38
38
|
dependencies = [
|
|
39
39
|
"pylhe>=1.0.0",
|
|
40
|
+
"typing-extensions>=4.0.0",
|
|
40
41
|
]
|
|
41
42
|
|
|
42
43
|
[project.optional-dependencies]
|
|
@@ -56,20 +57,7 @@ develop = [
|
|
|
56
57
|
"pre-commit",
|
|
57
58
|
"tbump>=6.7.0",
|
|
58
59
|
]
|
|
59
|
-
|
|
60
|
-
"sphinx",
|
|
61
|
-
"sphinx-copybutton",
|
|
62
|
-
"sphinx-rtd-theme>=0.5.0",
|
|
63
|
-
"nbsphinx",
|
|
64
|
-
"jupyter-sphinx",
|
|
65
|
-
"sphinx-autoapi",
|
|
66
|
-
"sphinx-autobuild",
|
|
67
|
-
"sphinx-math-dollar",
|
|
68
|
-
"pandoc",
|
|
69
|
-
"myst-nb",
|
|
70
|
-
"myst-parser",
|
|
71
|
-
"toml",
|
|
72
|
-
]
|
|
60
|
+
|
|
73
61
|
|
|
74
62
|
[project.urls]
|
|
75
63
|
Homepage = "https://github.com/APN-Pucky/lheutils"
|
|
@@ -80,6 +68,7 @@ Homepage = "https://github.com/APN-Pucky/lheutils"
|
|
|
80
68
|
[project.scripts]
|
|
81
69
|
lhecheck = "lheutils.cli.lhecheck:main"
|
|
82
70
|
lhefilter = "lheutils.cli.lhefilter:main"
|
|
71
|
+
lhefix = "lheutils.cli.lhefix:main"
|
|
83
72
|
lheinfo = "lheutils.cli.lheinfo:main"
|
|
84
73
|
lheshow = "lheutils.cli.lheshow:main"
|
|
85
74
|
lhesplit = "lheutils.cli.lhesplit:main"
|
|
@@ -87,6 +76,7 @@ lhemerge = "lheutils.cli.lhemerge:main"
|
|
|
87
76
|
lhestack = "lheutils.cli.lhestack:main"
|
|
88
77
|
lheunstack = "lheutils.cli.lheunstack:main"
|
|
89
78
|
lhe2lhe = "lheutils.cli.lhe2lhe:main"
|
|
79
|
+
lhediff = "lheutils.cli.lhediff:main"
|
|
90
80
|
|
|
91
81
|
[tool.hatch.version]
|
|
92
82
|
source = "vcs"
|
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '0.0.
|
|
32
|
-
__version_tuple__ = version_tuple = (0, 0,
|
|
31
|
+
__version__ = version = '0.0.4'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 0, 4)
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
|
@@ -14,7 +14,6 @@ from typing import Optional
|
|
|
14
14
|
|
|
15
15
|
import pylhe
|
|
16
16
|
|
|
17
|
-
import lheutils
|
|
18
17
|
from lheutils.cli.util import create_base_parser
|
|
19
18
|
|
|
20
19
|
# We do not want a Python Exception on broken pipe, which happens when piping to 'head' or 'less'
|
|
@@ -117,8 +116,9 @@ Weight formats:
|
|
|
117
116
|
parser.add_argument(
|
|
118
117
|
"--compress",
|
|
119
118
|
"-c",
|
|
120
|
-
action=
|
|
119
|
+
action=argparse.BooleanOptionalAction,
|
|
121
120
|
help="Compress the output file (ignored if output filename ends with .gz/.gzip)",
|
|
121
|
+
default=False,
|
|
122
122
|
)
|
|
123
123
|
|
|
124
124
|
parser.add_argument(
|
|
@@ -17,9 +17,8 @@ from dataclasses import dataclass
|
|
|
17
17
|
from pathlib import Path
|
|
18
18
|
from typing import Any, Optional, TextIO, Union
|
|
19
19
|
|
|
20
|
-
from typing_extensions import Self
|
|
21
|
-
|
|
22
20
|
import pylhe
|
|
21
|
+
from typing_extensions import Self
|
|
23
22
|
|
|
24
23
|
from lheutils.cli.util import create_base_parser
|
|
25
24
|
|
|
@@ -522,9 +521,10 @@ Examples:
|
|
|
522
521
|
)
|
|
523
522
|
|
|
524
523
|
parser.add_argument(
|
|
525
|
-
"--
|
|
526
|
-
action=
|
|
524
|
+
"--total-momentum",
|
|
525
|
+
action=argparse.BooleanOptionalAction,
|
|
527
526
|
help="Skip total momentum conservation checks",
|
|
527
|
+
default=True,
|
|
528
528
|
)
|
|
529
529
|
parser.add_argument(
|
|
530
530
|
"--total-momentum-rel",
|
|
@@ -540,9 +540,10 @@ Examples:
|
|
|
540
540
|
)
|
|
541
541
|
|
|
542
542
|
parser.add_argument(
|
|
543
|
-
"--
|
|
544
|
-
action=
|
|
543
|
+
"--onshell",
|
|
544
|
+
action=argparse.BooleanOptionalAction,
|
|
545
545
|
help="Skip on-shell mass checks",
|
|
546
|
+
default=True,
|
|
546
547
|
)
|
|
547
548
|
parser.add_argument(
|
|
548
549
|
"--onshell-rel",
|
|
@@ -558,9 +559,10 @@ Examples:
|
|
|
558
559
|
)
|
|
559
560
|
|
|
560
561
|
parser.add_argument(
|
|
561
|
-
"--
|
|
562
|
-
action=
|
|
562
|
+
"--positive-mass",
|
|
563
|
+
action=argparse.BooleanOptionalAction,
|
|
563
564
|
help="Skip positive mass checks",
|
|
565
|
+
default=True,
|
|
564
566
|
)
|
|
565
567
|
parser.add_argument(
|
|
566
568
|
"--positive-mass-abs",
|
|
@@ -595,12 +597,12 @@ Examples:
|
|
|
595
597
|
sys.exit(1)
|
|
596
598
|
|
|
597
599
|
lhecargs = LHECheckArgs(
|
|
598
|
-
positive_mass=
|
|
600
|
+
positive_mass=args.positive_mass,
|
|
599
601
|
positive_mass_abs=args.positive_mass_abs,
|
|
600
|
-
onshell=
|
|
602
|
+
onshell=args.onshell,
|
|
601
603
|
onshell_rel=args.onshell_rel,
|
|
602
604
|
onshell_abs=args.onshell_abs,
|
|
603
|
-
total_momentum=
|
|
605
|
+
total_momentum=args.total_momentum,
|
|
604
606
|
total_momentum_rel=args.total_momentum_rel,
|
|
605
607
|
total_momentum_abs=args.total_momentum_abs,
|
|
606
608
|
)
|
|
@@ -16,9 +16,8 @@ from itertools import zip_longest
|
|
|
16
16
|
from pathlib import Path
|
|
17
17
|
from typing import Any
|
|
18
18
|
|
|
19
|
-
from typing_extensions import Self
|
|
20
|
-
|
|
21
19
|
import pylhe
|
|
20
|
+
from typing_extensions import Self
|
|
22
21
|
|
|
23
22
|
from lheutils.cli.util import create_base_parser
|
|
24
23
|
|
|
@@ -471,12 +470,10 @@ def main() -> None:
|
|
|
471
470
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
472
471
|
epilog="""
|
|
473
472
|
Examples:
|
|
474
|
-
lhediff file1.lhe file2.lhe
|
|
475
|
-
lhediff file1.lhe file2.lhe --
|
|
476
|
-
lhediff file1.lhe file2.lhe --detailed -n 100 # Compare only first 100 events
|
|
477
|
-
lhediff file1.lhe file2.lhe --abs-tol 1e-10 # Allow small absolute differences
|
|
473
|
+
lhediff file1.lhe file2.lhe # Basic comparison (init + event counts)
|
|
474
|
+
lhediff file1.lhe file2.lhe --abs-tol 1e-10 # Allow small absolute differences
|
|
478
475
|
lhediff file1.lhe file2.lhe --rel-tol 1e-6 --abs-tol 1e-12 # Use both relative and absolute tolerance
|
|
479
|
-
lhediff original.lhe merged.lhe --
|
|
476
|
+
lhediff original.lhe merged.lhe --rel-tol 1e-10 # Check merge with numeric tolerance
|
|
480
477
|
""",
|
|
481
478
|
)
|
|
482
479
|
|
|
@@ -485,7 +482,7 @@ Examples:
|
|
|
485
482
|
parser.add_argument("file2", help="Second LHE file to compare")
|
|
486
483
|
|
|
487
484
|
parser.add_argument(
|
|
488
|
-
"--abs
|
|
485
|
+
"--abs",
|
|
489
486
|
"-a",
|
|
490
487
|
type=float,
|
|
491
488
|
default=1e-6,
|
|
@@ -493,7 +490,7 @@ Examples:
|
|
|
493
490
|
)
|
|
494
491
|
|
|
495
492
|
parser.add_argument(
|
|
496
|
-
"--rel
|
|
493
|
+
"--rel",
|
|
497
494
|
"-r",
|
|
498
495
|
type=float,
|
|
499
496
|
default=1e-6,
|
|
@@ -501,26 +498,23 @@ Examples:
|
|
|
501
498
|
)
|
|
502
499
|
|
|
503
500
|
parser.add_argument(
|
|
504
|
-
"--
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
default
|
|
508
|
-
help="Don't compare initialization sections (default: False)",
|
|
501
|
+
"--init",
|
|
502
|
+
action=argparse.BooleanOptionalAction,
|
|
503
|
+
default=True,
|
|
504
|
+
help="Don't compare initialization sections (default: True)",
|
|
509
505
|
)
|
|
510
506
|
|
|
511
507
|
parser.add_argument(
|
|
512
|
-
"--
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
default
|
|
516
|
-
help="Don't compare events (default: False)",
|
|
508
|
+
"--events",
|
|
509
|
+
action=argparse.BooleanOptionalAction,
|
|
510
|
+
default=True,
|
|
511
|
+
help="Don't compare events (default: True)",
|
|
517
512
|
)
|
|
518
513
|
|
|
519
514
|
parser.add_argument(
|
|
520
|
-
"--
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
default=False,
|
|
515
|
+
"--weights",
|
|
516
|
+
action=argparse.BooleanOptionalAction,
|
|
517
|
+
default=True,
|
|
524
518
|
help="Don't compare weight groups and weights in detail (default: True)",
|
|
525
519
|
)
|
|
526
520
|
|
|
@@ -540,11 +534,11 @@ Examples:
|
|
|
540
534
|
lhefilediff = diff_lhe_files(
|
|
541
535
|
args.file1,
|
|
542
536
|
args.file2,
|
|
543
|
-
init=
|
|
544
|
-
weights=
|
|
545
|
-
events=
|
|
546
|
-
abs_tol=args.
|
|
547
|
-
rel_tol=args.
|
|
537
|
+
init=args.init,
|
|
538
|
+
weights=args.weights,
|
|
539
|
+
events=args.events,
|
|
540
|
+
abs_tol=args.abs,
|
|
541
|
+
rel_tol=args.rel,
|
|
548
542
|
)
|
|
549
543
|
|
|
550
544
|
lhead = lhefilediff.print()
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
CLI tool to fix broken LHE files.
|
|
4
|
+
|
|
5
|
+
This tool can read from stdin or fix multiple files in place by iterating through
|
|
6
|
+
them as a generator until completion or exception.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import argparse
|
|
10
|
+
import os
|
|
11
|
+
import sys
|
|
12
|
+
import tempfile
|
|
13
|
+
from collections.abc import Iterable
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Optional
|
|
16
|
+
|
|
17
|
+
import pylhe
|
|
18
|
+
|
|
19
|
+
from lheutils.cli.util import create_base_parser
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def fix_file_inplace(
|
|
23
|
+
filepath: str, compress: bool = False, suffix: Optional[str] = None
|
|
24
|
+
) -> None:
|
|
25
|
+
"""Fix an LHE file in place using temporary file for safety.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
filepath: Path to the LHE file to fix.
|
|
29
|
+
compress: Whether to gzip-compress the output file.
|
|
30
|
+
suffix: Suffix to add to the output filename (None means replace original).
|
|
31
|
+
"""
|
|
32
|
+
try:
|
|
33
|
+
# Read the original file
|
|
34
|
+
lhefile = pylhe.LHEFile.fromfile(filepath)
|
|
35
|
+
|
|
36
|
+
# Determine output filename
|
|
37
|
+
filepath_obj = Path(filepath)
|
|
38
|
+
if suffix is None:
|
|
39
|
+
# Replace original file
|
|
40
|
+
output_path = filepath
|
|
41
|
+
suffix = filepath_obj.suffix
|
|
42
|
+
else:
|
|
43
|
+
# Create new filename with prefix/suffix
|
|
44
|
+
new_name = f"{filepath_obj.stem}{suffix}"
|
|
45
|
+
output_path = str(filepath_obj.parent / new_name)
|
|
46
|
+
|
|
47
|
+
# Create temporary file in same directory as original
|
|
48
|
+
temp_fd, temp_path = tempfile.mkstemp(
|
|
49
|
+
suffix=".tmp" + suffix,
|
|
50
|
+
prefix=filepath_obj.stem + "_",
|
|
51
|
+
dir=filepath_obj.parent,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
try:
|
|
55
|
+
# Close the file descriptor since pylhe will open its own
|
|
56
|
+
os.close(temp_fd)
|
|
57
|
+
# Get original file permissions if replacing the file
|
|
58
|
+
original_stat = os.stat(filepath)
|
|
59
|
+
# Set temporary file permissions to match original
|
|
60
|
+
os.chmod(temp_path, original_stat.st_mode)
|
|
61
|
+
|
|
62
|
+
# Filter events and write to temporary file
|
|
63
|
+
def _generator() -> Iterable[pylhe.LHEEvent]:
|
|
64
|
+
event_count = 0
|
|
65
|
+
try:
|
|
66
|
+
for event in lhefile.events:
|
|
67
|
+
event_count += 1
|
|
68
|
+
yield event
|
|
69
|
+
except Exception as e:
|
|
70
|
+
print(
|
|
71
|
+
f"{filepath} terminating LHE file at event {event_count} due to: {e}"
|
|
72
|
+
)
|
|
73
|
+
return
|
|
74
|
+
finally:
|
|
75
|
+
print(
|
|
76
|
+
f"{filepath} fixed: processed {event_count} events -> {output_path}"
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# Write to temporary file
|
|
80
|
+
pylhe.LHEFile(init=lhefile.init, events=_generator()).tofile(
|
|
81
|
+
temp_path, gz=compress
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
# Atomically replace/create output file with temporary file
|
|
85
|
+
os.replace(temp_path, output_path)
|
|
86
|
+
|
|
87
|
+
except Exception as e:
|
|
88
|
+
# Clean up temporary file on error
|
|
89
|
+
if os.path.exists(temp_path):
|
|
90
|
+
os.unlink(temp_path)
|
|
91
|
+
raise e
|
|
92
|
+
|
|
93
|
+
except Exception as e:
|
|
94
|
+
print(f"Error fixing {filepath}: {e}", file=sys.stderr)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def fix_from_stdin() -> None:
|
|
98
|
+
"""Fix LHE content from stdin and output to stdout."""
|
|
99
|
+
try:
|
|
100
|
+
lhefile = pylhe.LHEFile.frombuffer(sys.stdin)
|
|
101
|
+
|
|
102
|
+
# Create generator that handles exceptions gracefully
|
|
103
|
+
def _generator() -> Iterable[pylhe.LHEEvent]:
|
|
104
|
+
try:
|
|
105
|
+
yield from lhefile.events
|
|
106
|
+
except Exception:
|
|
107
|
+
# Terminate silently on exception for chaining compatibility
|
|
108
|
+
return
|
|
109
|
+
|
|
110
|
+
# Write fixed LHE to stdout
|
|
111
|
+
pylhe.LHEFile(init=lhefile.init, events=_generator()).write(sys.stdout)
|
|
112
|
+
|
|
113
|
+
except Exception as e:
|
|
114
|
+
print(f"Error processing stdin: {e}", file=sys.stderr)
|
|
115
|
+
sys.exit(1)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def main() -> None:
|
|
119
|
+
"""Main entry point for lhefix CLI."""
|
|
120
|
+
parser = create_base_parser(
|
|
121
|
+
prog="lhefix",
|
|
122
|
+
description="Fix broken LHE files. Reads from stdin by default or fixes multiple files in place. "
|
|
123
|
+
"For parallel processing, use GNU parallel: 'parallel -j 8 lhefix ::: *.lhe'.",
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
parser.add_argument(
|
|
127
|
+
"--suffix",
|
|
128
|
+
default=".fix.lhe.gz",
|
|
129
|
+
help="Suffix to add to use for filenames (default: '.fix.lhe.gz').",
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
parser.add_argument(
|
|
133
|
+
"--compress",
|
|
134
|
+
"-c",
|
|
135
|
+
action=argparse.BooleanOptionalAction,
|
|
136
|
+
help="Compress output files.",
|
|
137
|
+
default=False,
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
parser.add_argument(
|
|
141
|
+
"files",
|
|
142
|
+
nargs="*",
|
|
143
|
+
help="LHE files to fix in place. If no files provided, reads from stdin.",
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
args = parser.parse_args()
|
|
147
|
+
|
|
148
|
+
if not args.files:
|
|
149
|
+
# No files provided, read from stdin
|
|
150
|
+
fix_from_stdin()
|
|
151
|
+
else:
|
|
152
|
+
# Fix multiple files in place
|
|
153
|
+
for filepath in args.files:
|
|
154
|
+
if not Path(filepath).exists():
|
|
155
|
+
print(f"Error: File not found: {filepath}", file=sys.stderr)
|
|
156
|
+
continue
|
|
157
|
+
fix_file_inplace(filepath, args.compress, args.suffix)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
if __name__ == "__main__":
|
|
161
|
+
main()
|