isgri 0.7.0__py3-none-any.whl → 0.7.2__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.
isgri/__version__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.7.0"
1
+ __version__ = "0.7.2"
isgri/catalog/builder.py CHANGED
@@ -306,7 +306,14 @@ class CatalogBuilder:
306
306
  data = Parallel(n_jobs=self.n_cores, backend="multiprocessing")(
307
307
  delayed(self._process_scw)(path) for path in rev_paths
308
308
  )
309
- table_data_list, array_data_dicts = zip(*[d for d in data if d[0] is not None])
309
+
310
+ # Filter out None results
311
+ valid_data = [d for d in data if d[0] is not None]
312
+
313
+ if not valid_data:
314
+ return None, None
315
+
316
+ table_data_list, array_data_dicts = zip(*valid_data)
310
317
 
311
318
  dtype = [("SWID", "U16"), ("TIME", "O"), ("COUNTS", "O"), ("MODULE_COUNTS", "O"), ("GTIS", "O")]
312
319
  array_data = np.empty(len(array_data_dicts), dtype=dtype)
@@ -426,6 +433,11 @@ class CatalogBuilder:
426
433
  for revolution, rev_paths in revolutions.items():
427
434
  print(f"Processing revolution {revolution} with {len(rev_paths)} ScWs...")
428
435
  table_data_rows, array_data_list = self._process_rev(rev_paths)
436
+
437
+ if table_data_rows is None:
438
+ print(f"No valid ScWs found in revolution {revolution}, skipping.")
439
+ continue
440
+
429
441
  print(f"Adding {len(table_data_rows)} ScWs from revolution {revolution} to catalog.")
430
442
  self._add_catalog_data(table_data_rows)
431
443
  if self.lightcurve_cache is not None:
isgri/cli/builder.py ADDED
@@ -0,0 +1,102 @@
1
+ import click
2
+ from pathlib import Path
3
+ from ..catalog.builder import CatalogBuilder
4
+ from ..config import Config
5
+
6
+
7
+ @click.command()
8
+ @click.option(
9
+ "--archive", type=click.Path(), help="Path to INTEGRAL archive directory. If not provided, uses config value."
10
+ )
11
+ @click.option("--catalog", type=click.Path(), help="Path to catalog FITS file. If not provided, uses config value.")
12
+ @click.option("--cache", type=click.Path(), help="Path to light curve cache directory. Optional.")
13
+ @click.option("--cores", type=int, help="Number of CPU cores for parallel processing. Defaults to all available.")
14
+ def update(archive, catalog, cache, cores):
15
+ """
16
+ Update science window catalog from archive.
17
+
18
+ Scans the INTEGRAL archive for new science windows not present in the catalog,
19
+ processes them in parallel by revolution, and adds results to the catalog.
20
+
21
+ If archive or catalog paths are not provided, uses values from configuration.
22
+ Optionally caches light curve arrays to the specified directory.
23
+
24
+ Examples:
25
+
26
+ Update using configured paths:
27
+
28
+ isgri update
29
+
30
+ Update with custom paths:
31
+
32
+ isgri update --archive /anita/archivio/ --catalog ~/data/catalog.fits
33
+
34
+ Update with light curve caching:
35
+
36
+ isgri update --cache ~/data/lightcurves/
37
+
38
+ Update using 4 CPU cores:
39
+
40
+ isgri update --cores 4
41
+ """
42
+ if archive is None or catalog is None:
43
+ cfg = Config()
44
+
45
+ if archive is None:
46
+ archive = cfg.archive_path
47
+ if not archive:
48
+ click.echo("Error: No archive path configured", err=True)
49
+ raise click.Abort()
50
+
51
+ if catalog is None:
52
+ catalog = cfg.catalog_path
53
+ if not catalog:
54
+ click.echo("Error: No catalog path configured", err=True)
55
+ raise click.Abort()
56
+
57
+ archive_path = Path(archive).expanduser().resolve()
58
+ catalog_path = Path(catalog).expanduser().resolve()
59
+ cache_path = Path(cache).expanduser().resolve() if cache else None
60
+
61
+ if not archive_path.exists():
62
+ click.echo(f"Error: Archive directory does not exist: {archive_path}", err=True)
63
+ raise click.Abort()
64
+
65
+ if not catalog_path.parent.exists():
66
+ click.echo(f"Warning: Catalog directory does not exist: {catalog_path.parent}", err=True)
67
+ if not click.confirm("Create new catalog file?"):
68
+ raise click.Abort()
69
+
70
+ if cache_path and not cache_path.exists():
71
+ click.echo(f"Warning: Cache directory does not exist: {cache_path}", err=True)
72
+ if not click.confirm("Create directory?"):
73
+ raise click.Abort()
74
+ cache_path.mkdir(parents=True, exist_ok=True)
75
+ click.echo(f"✓ Created cache directory: {cache_path}")
76
+
77
+ click.echo(f"Archive: {archive_path}")
78
+ click.echo(f"Catalog: {catalog_path}")
79
+ if cache_path:
80
+ click.echo(f"Cache: {cache_path}")
81
+ else:
82
+ click.echo("Cache: (none)")
83
+ if cores:
84
+ click.echo(f"CPU cores: {cores}")
85
+ click.echo()
86
+
87
+ try:
88
+ builder = CatalogBuilder(
89
+ archive_path=str(archive_path),
90
+ catalog_path=str(catalog_path),
91
+ lightcurve_cache=str(cache_path) if cache_path else None,
92
+ n_cores=cores,
93
+ )
94
+
95
+ builder.update_catalog()
96
+
97
+ click.echo()
98
+ click.echo("Catalog update complete")
99
+
100
+ except Exception as e:
101
+ click.echo(f"Error during catalog update: {e}", err=True)
102
+ raise click.Abort()
isgri/cli/main.py CHANGED
@@ -4,6 +4,7 @@ from ..catalog import ScwQuery
4
4
  from ..__version__ import __version__
5
5
  from ..config import Config
6
6
  from .query import query_direct, query_interactive
7
+ from .builder import update
7
8
 
8
9
 
9
10
  @click.group()
@@ -29,7 +30,10 @@ def main():
29
30
  )
30
31
  @click.option("--list-swids", is_flag=True, help="Only output SWID list")
31
32
  @click.option("--count", is_flag=True, help="Only show count")
32
- def query(catalog, tstart, tstop, ra, dec, radius, fov, max_chi, chi_type, revolution, output, list_swids, count):
33
+ @click.option("--columns", help="Comma-separated list of columns to save (e.g., SWID,TSTART,TSTOP)")
34
+ def query(
35
+ catalog, tstart, tstop, ra, dec, radius, fov, max_chi, chi_type, revolution, output, list_swids, count, columns
36
+ ):
33
37
  """
34
38
  Query INTEGRAL science window catalog.
35
39
 
@@ -58,6 +62,10 @@ def query(catalog, tstart, tstop, ra, dec, radius, fov, max_chi, chi_type, revol
58
62
 
59
63
  isgri query --tstart 3000 --tstop 3100 --output results.fits
60
64
 
65
+ Save specific columns:
66
+
67
+ isgri query --tstart 3000 --tstop 3100 --output results.fits --columns SWID,TSTART,TSTOP,CHI
68
+
61
69
  Get only SWID list:
62
70
 
63
71
  isgri query --tstart 3000 --tstop 3100 --list-swids
@@ -69,14 +77,26 @@ def query(catalog, tstart, tstop, ra, dec, radius, fov, max_chi, chi_type, revol
69
77
  if catalog is None:
70
78
  cfg = Config()
71
79
  catalog = cfg.catalog_path
72
-
73
- if not catalog:
74
- click.echo("Error: No catalog configured", err=True)
80
+ if catalog is None:
81
+ click.echo("Error: No catalog path configured", err=True)
75
82
  raise click.Abort()
76
83
 
77
84
  if any(param is not None for param in [tstart, tstop, ra, dec, radius, max_chi, revolution]):
78
85
  query_direct(
79
- catalog, tstart, tstop, ra, dec, radius, fov, max_chi, chi_type, revolution, output, list_swids, count
86
+ catalog,
87
+ tstart,
88
+ tstop,
89
+ ra,
90
+ dec,
91
+ radius,
92
+ fov,
93
+ max_chi,
94
+ chi_type,
95
+ revolution,
96
+ output,
97
+ list_swids,
98
+ count,
99
+ columns,
80
100
  )
81
101
  else:
82
102
  query_interactive(catalog)
@@ -181,5 +201,7 @@ def config_set(archive, catalog, pif):
181
201
  click.echo(f"Configuration saved to: {cfg.path}")
182
202
 
183
203
 
204
+ main.add_command(update)
205
+
184
206
  if __name__ == "__main__":
185
207
  main()
isgri/cli/query.py CHANGED
@@ -55,10 +55,10 @@ def match_command(user_input):
55
55
  commands = {
56
56
  "time": ["time", "t"],
57
57
  "pos": ["pos", "position", "p"],
58
- "quality": ["quality", "qual", "q"],
59
- "revolution": ["revolution", "rev", "r"],
58
+ "quality": ["quality", "qual"],
59
+ "revolution": ["revolution", "rev"],
60
60
  "show": ["show", "s", "display"],
61
- "reset": ["reset", "clear"],
61
+ "reset": ["reset", "clear", "r"],
62
62
  "save": ["save", "write"],
63
63
  "help": ["help", "h", "?"],
64
64
  "exit": ["exit", "quit", "q"],
@@ -77,7 +77,20 @@ def match_command(user_input):
77
77
 
78
78
 
79
79
  def query_direct(
80
- catalog_path, tstart, tstop, ra, dec, radius, fov, max_chi, chi_type, revolution, output, list_swids, count
80
+ catalog_path,
81
+ tstart,
82
+ tstop,
83
+ ra,
84
+ dec,
85
+ radius,
86
+ fov,
87
+ max_chi,
88
+ chi_type,
89
+ revolution,
90
+ output,
91
+ list_swids,
92
+ count,
93
+ columns,
81
94
  ):
82
95
  try:
83
96
  q = ScwQuery(catalog_path)
@@ -108,7 +121,17 @@ def query_direct(
108
121
  click.echo(q.count())
109
122
 
110
123
  elif output:
111
- q.write(output, overwrite=True, swid_only=list_swids)
124
+ col_list = None
125
+ if columns and not list_swids:
126
+ col_list = [c.strip() for c in columns.split(",")]
127
+ available = q.catalog.colnames
128
+ invalid = [c for c in col_list if c not in available]
129
+ if invalid:
130
+ click.echo(f"Error: Invalid columns: {invalid}", err=True)
131
+ click.echo(f"Available: {', '.join(available)}", err=True)
132
+ raise click.Abort()
133
+
134
+ q.write(output, overwrite=True, swid_only=list_swids, columns=col_list)
112
135
  click.echo(f"Saved {q.count()} SCWs to {output}")
113
136
 
114
137
  else:
@@ -196,9 +219,33 @@ def query_interactive(catalog_path):
196
219
  click.echo(f"→ {len(q.catalog)} SCWs")
197
220
  elif cmd == "save":
198
221
  swid_only = click.confirm("Save only SWID list?", default=False)
222
+
223
+ col_list = None
224
+ if not swid_only:
225
+ if click.confirm("Select specific columns?", default=False):
226
+ click.echo("\nAvailable columns:")
227
+ available = q.catalog.colnames
228
+ for i, col in enumerate(available, 1):
229
+ click.echo(f" {i:2d}. {col}")
230
+
231
+ click.echo("\nEnter column names (comma-separated) or leave empty for all:")
232
+ col_input = click.prompt("Columns", default="", show_default=False)
233
+
234
+ if col_input:
235
+ col_list = [c.strip() for c in col_input.split(",")]
236
+ invalid = [c for c in col_list if c not in available]
237
+ if invalid:
238
+ click.echo(f"Warning: Invalid columns ignored: {invalid}")
239
+ col_list = [c for c in col_list if c in available]
240
+
241
+ if not col_list:
242
+ click.echo("No valid columns selected, using all")
243
+ col_list = None
244
+
199
245
  path = click.prompt("File")
200
- q.write(path, overwrite=True, swid_only=swid_only)
201
- click.echo(f" Saved")
246
+ q.write(path, overwrite=True, swid_only=swid_only, columns=col_list)
247
+ click.echo(f"Saved {q.count()} SCWs to {path}")
248
+
202
249
  elif cmd is None:
203
250
  click.echo(f"Unknown command: {user_input}. Type 'help' for available commands.")
204
251
  else:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: isgri
3
- Version: 0.7.0
3
+ Version: 0.7.2
4
4
  Summary: Python package for INTEGRAL IBIS/ISGRI lightcurve analysis
5
5
  Author: Dominik Patryk Pacholski
6
6
  License: MIT
@@ -26,6 +26,7 @@ Query catalogs directly from the terminal:
26
26
  - Interactive and direct query modes
27
27
  - Filter by time, position, quality, revolution
28
28
  - Export results to FITS/CSV or SWID lists
29
+ - Update catalogs with new science windows from archive
29
30
 
30
31
  ### SCW Catalog Query
31
32
  Query INTEGRAL Science Window catalogs with a fluent Python API:
@@ -33,6 +34,13 @@ Query INTEGRAL Science Window catalogs with a fluent Python API:
33
34
  - Calculate detector offsets
34
35
  - Export results to any auto detectable astropy table extension or in table aligned data for any other extension
35
36
 
37
+ ### Catalog Builder
38
+ Build and update INTEGRAL/ISGRI science window catalogs:
39
+ - Automatic discovery of new science windows in archive
40
+ - Parallel processing of quality metrics
41
+ - Optional light curve caching
42
+ - Incremental catalog updates
43
+
36
44
  ### Light Curve Analysis
37
45
  Extract and analyze ISGRI light curves:
38
46
  - Load from file paths or SWID/source lookup
@@ -60,6 +68,9 @@ isgri config-set --archive /path/to/archive --catalog ~/data/scw_catalog.fits --
60
68
  # View current config
61
69
  isgri config
62
70
 
71
+ # Update catalog from archive
72
+ isgri update
73
+
63
74
  # Interactive catalog query
64
75
  isgri query
65
76
  # query> time
@@ -109,6 +120,51 @@ swids = cat.get_swids()
109
120
  print(f"Found {len(results)} observations")
110
121
  ```
111
122
 
123
+ ### Build/Update SCW Catalog
124
+
125
+ #### Command Line
126
+
127
+ ```bash
128
+ # Update catalog using configured paths
129
+ isgri update
130
+
131
+ # Update with custom paths
132
+ isgri update --archive /anita/archivio/ --catalog ~/data/catalog.fits
133
+
134
+ # Enable light curve caching (15-1000 keV, 1s bins)
135
+ isgri update --cache ~/data/lightcurves/
136
+
137
+ # Limit CPU cores for parallel processing
138
+ isgri update --cores 4
139
+ ```
140
+
141
+ #### Python API
142
+
143
+ ```python
144
+ from isgri.catalog import CatalogBuilder
145
+
146
+ # Create builder instance
147
+ builder = CatalogBuilder(
148
+ archive_path="/path/to/archive",
149
+ catalog_path="scw_catalog.fits",
150
+ lightcurve_cache="/path/to/cache", # optional
151
+ n_cores=8
152
+ )
153
+
154
+ # Update catalog with new science windows
155
+ builder.update_catalog()
156
+
157
+ # Find all science windows in archive
158
+ swids, paths = builder.find_scws()
159
+ print(f"Found {len(swids)} science windows")
160
+ ```
161
+
162
+ The builder:
163
+ - Scans archive for new ScWs not in catalog
164
+ - Computes quality metrics (raw, sigma-clipped, GTI-filtered chi-squared)
165
+ - Processes in parallel by revolution
166
+ - Optionally caches 1s light curves (15-1000 keV)
167
+
112
168
  ### Analyze Light Curves
113
169
 
114
170
  ```python
@@ -1,21 +1,22 @@
1
1
  isgri/__init__.py,sha256=V2hnOxXKcjMiusdGP8sOAR4QsBlWHQ0pZZMN2Cean6o,38
2
- isgri/__version__.py,sha256=P1kRuZW02tF-ekLuBqSjV_tIKKqcbfRPDdpp9C8ftUY,21
2
+ isgri/__version__.py,sha256=ISWV9_SJtaCeILz1FAMYHSjVkHbV08KnnT23Ww0_w-o,21
3
3
  isgri/config.py,sha256=8hziHnQN_dnS3FkOtrPUUV1QLJ9HmGiPzDuAiq3GoXc,5044
4
4
  isgri/catalog/__init__.py,sha256=IGfzGcGx1F1zplj7YIUuzE5Cvvxg7XJi0zu0aBXI0-A,113
5
- isgri/catalog/builder.py,sha256=EDda0QIHBRwh1nPUndSYfw4nvJHxjXPmVgyPscksBrM,15490
5
+ isgri/catalog/builder.py,sha256=J1Z_p699hoQtiQjYw5-jvf0aSIIOcpe8djbCjh-NOng,15779
6
6
  isgri/catalog/scwquery.py,sha256=v7nR6-6PmYaBunFeGtmDWPqDkUBUvwjv2jtM1RbGQx4,20397
7
7
  isgri/catalog/wcs.py,sha256=mD6bZxiBxKYpuYCl8f2tSCc8uuWFzMRL2jf5SuFAhfg,5562
8
8
  isgri/cli/__init__.py,sha256=SCIcTdOvfEkrZd1doOtOnvCj8PmdRuXu18YYJQxfFrs,24
9
- isgri/cli/main.py,sha256=EfBN4E9oUvhB0GfQldLU3eSvmRC4JjxM72qWFEc2_IA,6379
10
- isgri/cli/query.py,sha256=jIAJ8Ghr73OivSIqTjqYE-y-gn9hw4-QpEeH-z6Bny0,8012
9
+ isgri/cli/builder.py,sha256=dCp9ECl9MoThTRXAkXXazafHYSMQF0FLHKDzlWwT7vE,3494
10
+ isgri/cli/main.py,sha256=gOK8UDOviwb_o4YqhnZY_zsytNxNe7NZNWD7yu1rSrM,6883
11
+ isgri/cli/query.py,sha256=o2AV8pwLNgRL2goAppVT0z_gZApLy5_5CjtzA4iqYs8,9830
11
12
  isgri/utils/__init__.py,sha256=H83Al7urc6LNW5KUzUBRdtRBUTahiZmkehKFiK90RrU,183
12
13
  isgri/utils/file_loaders.py,sha256=E4-0QA6r1QAX1gV1Dd5qhKPDXZIdSEk7AWdOQrovHGo,17423
13
14
  isgri/utils/lightcurve.py,sha256=v_JNLzf1SBvFLzrU7ZXpSj44eRKaZChiO4gvS2_O35Y,17408
14
15
  isgri/utils/pif.py,sha256=sAtzQhe3spLS41UkP1yZsqGPxe3IHLmIGA_d3bVg2v4,9121
15
16
  isgri/utils/quality.py,sha256=7CbYaNstoX60AYz2_ym-xjMkiDLpkYgQfaTStYuQZtk,13409
16
17
  isgri/utils/time_conversion.py,sha256=MNPVjrsrmwRDbCWmqdWN0xRs8PtHkFGli-H2cYwF9Ns,5204
17
- isgri-0.7.0.dist-info/METADATA,sha256=7MSVI7E4DdtSpm7GZXdYJ-SD1mTTddf0sewrJNkgYcE,5115
18
- isgri-0.7.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
19
- isgri-0.7.0.dist-info/entry_points.txt,sha256=aM2K4RGihbwsj9crjPG-BvWhErcdtZt3tJqT6AaOojU,46
20
- isgri-0.7.0.dist-info/licenses/LICENSE,sha256=Q8oxmHR1cSnEXSHCjY3qeXMtupZI_1ZQZ1MBt4oeANE,1102
21
- isgri-0.7.0.dist-info/RECORD,,
18
+ isgri-0.7.2.dist-info/METADATA,sha256=Z3bs7SF2VmrGBMBjBSZGcLorkqRj4ild1NFDOpY6B1c,6499
19
+ isgri-0.7.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
20
+ isgri-0.7.2.dist-info/entry_points.txt,sha256=aM2K4RGihbwsj9crjPG-BvWhErcdtZt3tJqT6AaOojU,46
21
+ isgri-0.7.2.dist-info/licenses/LICENSE,sha256=Q8oxmHR1cSnEXSHCjY3qeXMtupZI_1ZQZ1MBt4oeANE,1102
22
+ isgri-0.7.2.dist-info/RECORD,,
File without changes