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.
Files changed (27) hide show
  1. {lheutils-0.0.2 → lheutils-0.0.4}/PKG-INFO +7 -18
  2. {lheutils-0.0.2 → lheutils-0.0.4}/README.md +4 -3
  3. {lheutils-0.0.2 → lheutils-0.0.4}/pyproject.toml +5 -15
  4. lheutils-0.0.4/src/lheutils/__init__.py +3 -0
  5. {lheutils-0.0.2 → lheutils-0.0.4}/src/lheutils/_version.py +2 -2
  6. lheutils-0.0.4/src/lheutils/_version.pyi +2 -0
  7. {lheutils-0.0.2 → lheutils-0.0.4}/src/lheutils/cli/lhe2lhe.py +2 -2
  8. {lheutils-0.0.2 → lheutils-0.0.4}/src/lheutils/cli/lhecheck.py +13 -11
  9. {lheutils-0.0.2 → lheutils-0.0.4}/src/lheutils/cli/lhediff.py +22 -28
  10. lheutils-0.0.4/src/lheutils/cli/lhefix.py +161 -0
  11. {lheutils-0.0.2 → lheutils-0.0.4}/src/lheutils/cli/lheinfo.py +155 -104
  12. {lheutils-0.0.2 → lheutils-0.0.4}/src/lheutils/cli/lhemerge.py +39 -32
  13. lheutils-0.0.4/src/lheutils/cli/lheshow.py +158 -0
  14. lheutils-0.0.4/src/lheutils/cli/lhesplit.py +153 -0
  15. {lheutils-0.0.2 → lheutils-0.0.4}/src/lheutils/cli/util.py +7 -5
  16. {lheutils-0.0.2 → lheutils-0.0.4}/tests/cli/test_lhe2lhe.py +4 -4
  17. {lheutils-0.0.2 → lheutils-0.0.4}/tests/cli/test_lhecheck.py +2 -1
  18. {lheutils-0.0.2 → lheutils-0.0.4}/tests/cli/test_lhediff.py +2 -1
  19. lheutils-0.0.4/tests/cli/test_lhesplit.py +0 -0
  20. lheutils-0.0.2/src/lheutils/__init__.py +0 -1
  21. lheutils-0.0.2/src/lheutils/cli/lheshow.py +0 -118
  22. lheutils-0.0.2/src/lheutils/cli/lhesplit.py +0 -132
  23. {lheutils-0.0.2 → lheutils-0.0.4}/.gitignore +0 -0
  24. {lheutils-0.0.2 → lheutils-0.0.4}/LICENSE +0 -0
  25. {lheutils-0.0.2 → lheutils-0.0.4}/src/lheutils/cli/lhefilter.py +0 -0
  26. {lheutils-0.0.2 → lheutils-0.0.4}/src/lheutils/cli/lhestack.py +0 -0
  27. {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.2
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 <a_neuw01@unimib.it>
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.2
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.2
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 = "a_neuw01@unimib.it" },
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
- docs = [
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"
@@ -0,0 +1,3 @@
1
+ from lheutils._version import version as __version__
2
+
3
+ __all__ = ["__version__"]
@@ -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.2'
32
- __version_tuple__ = version_tuple = (0, 0, 2)
31
+ __version__ = version = '0.0.4'
32
+ __version_tuple__ = version_tuple = (0, 0, 4)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -0,0 +1,2 @@
1
+ version: str
2
+ version_tuple: tuple[int, int, int] | tuple[int, int, int, str, str]
@@ -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="store_true",
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
- "--no-total-momentum",
526
- action="store_true",
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
- "--no-onshell",
544
- action="store_true",
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
- "--no-positive-mass",
562
- action="store_true",
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=not args.no_positive_mass,
600
+ positive_mass=args.positive_mass,
599
601
  positive_mass_abs=args.positive_mass_abs,
600
- onshell=not args.no_onshell,
602
+ onshell=args.onshell,
601
603
  onshell_rel=args.onshell_rel,
602
604
  onshell_abs=args.onshell_abs,
603
- total_momentum=not args.no_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 # Basic comparison (init + event counts)
475
- lhediff file1.lhe file2.lhe --detailed # Detailed event-by-event comparison
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 --detailed --rel-tol 1e-10 # Check merge with numeric tolerance
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-tol",
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-tol",
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
- "--no-init",
505
- "-ni",
506
- action="store_true",
507
- default=False,
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
- "--no-events",
513
- "-ne",
514
- action="store_true",
515
- default=False,
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
- "--no-weights",
521
- "-nw",
522
- action="store_true",
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=not args.no_init,
544
- weights=not args.no_weights,
545
- events=not args.no_events,
546
- abs_tol=args.abs_tol,
547
- rel_tol=args.rel_tol,
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()