fits2tmp 0.2.0__tar.gz → 0.4.0__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.
@@ -0,0 +1,93 @@
1
+ Metadata-Version: 2.3
2
+ Name: fits2tmp
3
+ Version: 0.4.0
4
+ Summary: A way of loading FITS files into temporary filesystems to make them available in memory.
5
+ License: BSD-3-Clause
6
+ Author: Verity Allan
7
+ Author-email: vla22@cam.ac.uk
8
+ Requires-Python: >=3.10,<4.0
9
+ Classifier: License :: OSI Approved :: BSD License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Requires-Dist: aiohttp (>=3.13.3,<4.0.0)
16
+ Requires-Dist: astropy (>=6.1.4,<7.0.0)
17
+ Requires-Dist: dask[distributed] (>=2025.1.0,<2026.0.0)
18
+ Requires-Dist: fsspec (>=2026.2.0,<2027.0.0)
19
+ Requires-Dist: h5py (>=3.12.1,<4.0.0)
20
+ Requires-Dist: numpy (>=2.1.2,<3.0.0)
21
+ Requires-Dist: pytest (>=8.3.3,<9.0.0)
22
+ Requires-Dist: requests (>=2.32.5,<3.0.0)
23
+ Requires-Dist: s3fs (>=2026.2.0,<2027.0.0)
24
+ Requires-Dist: zarr (>=2.18.3,<3.0.0)
25
+ Project-URL: Repository, https://gitlab.developers.cam.ac.uk/phy/ra/vla22-phd/fits2tmp
26
+ Description-Content-Type: text/markdown
27
+
28
+ # fits2tmp
29
+
30
+ This project allows you to take a large FITS file and render it as a set of smaller FITS files stored in-memory in a temporary directory, with appropriately updated headers.
31
+ The program returns a list of the location of these files, which can then be processed, by other programs,perhaps by a parallel processing framework such as Dask, or by using Python's `subprocess` module to allow feeding those files to a non-Python program with a command line interface (CLI).
32
+
33
+ The program also allows you to turn a zarr file containing your FITS data into small FITS files, provided you have extracted the FITS file header to a text file.
34
+
35
+ This can also be done for hdf5 files (again assuming FITS file header data is saved to a text file). Note that if you attempt to do this with Dask running on multiple nodes, it will currently fail, because of a bug in Dask.
36
+
37
+ Utilities in the program can be used to write headers out to or read them from a text file.
38
+
39
+ ## TODO:
40
+
41
+ - Add a utility to convert FITS files to zarr, including saving the header out to a text file.
42
+ - Add hdf5 support, using more native hdf5 metadata storage solutions.
43
+ - Add xradio/xarray support.
44
+
45
+ ## Installing fits2tmp
46
+
47
+ The program is available from pypi so can be installed with pip: `pip install fits2tmp`.
48
+
49
+ ## Example Usage
50
+
51
+ ```
52
+ import fits2tmp as f2t
53
+
54
+ a = f2t.fitstmpfiles("fits", "/path/to/input/data", "/path/to/temporary/filesystem", "path/to/header/", (100, 100, 100), chunks=(20,20,20))
55
+ ```
56
+ fitstmpfiles takes a large input data set in one of the supported formats, and returns smaller FITS files to a specified directory.
57
+
58
+ The parameters for fitstmpfiles are:
59
+
60
+ - filetype: a string describing the filetype; currently supports "fits" and "zarr" fully; "hdf5" only supported on single node runs.
61
+ - inputpath: path to the input data
62
+ - destination: path to destination directory. It is intended that temporary directories are used, to try to keep the data in-memory, rather than doing potentially expensive disk writes; however, if you wish to write to more permanent storage, that is possible. You will probably want to clean up manually once the run is done, though.
63
+ - headerloc: defaults to `None`. If loading from a FITS file, this isn't required; if you are loading from zarr or hdf5, you need to provide the path to a text file containing the FITS headers. It is recommended that you use the fits2tmp utilities to prepare the header file.
64
+ - chunks: defaults to `None`. You will want to set this to some value that makes sense for the resources on which you're processing the data. Dask recommend that you have at least 10 times as much RAM as the size of the chunk you're processing. You will also want to consider how much overlap you are using when setting the chunk size. If you are particularly concerned about file loading performance, then consider setting the chunk size to a multiple of the block size used in the storage.
65
+ - overlap: defaults to `None`. You will want to set this to some size that makes sense for the data processing you're doing. You probably want to set this to approximately the size of the largest Gaussian kernel you plan to use to detect objects/features.
66
+
67
+ ## Utilities
68
+
69
+ These small methods are provided to make it easy to write out a FITS header to a file, to allow provision of FITS metadata when using other storage solutions.
70
+ To create a header from an existing FITS file:
71
+
72
+ ```
73
+ from astropy.io import fits
74
+ import fits2tmp as f2t
75
+
76
+ with fits.open('path/to/fits/file') as f:
77
+ header = f[0].header
78
+ d = f2t.header_dict(header)
79
+ f2t.dict2file(d, 'path/to/where/you/save/header')
80
+ ```
81
+
82
+ Note that this process strips most of the history from the header; if you wish to update the history, use some of the astropy.io.fits tools to accomplish this. This tool is purely to provide a minimal amount of metadata to allow storing FITS data in zarr and similar file formats, and also to allow processing of sections of FITS files where it's not desirable to have all processes reading from the same file in storage.
83
+
84
+ To create a header from text file containing a json dump of a dictionary created from a FITS header:
85
+
86
+ ```
87
+ import fits2tmp as ft2
88
+
89
+ loaded_header_dict = f2t.file2dict('/path/to/header/txt/file')
90
+ header = dict2header(loaded_header_dict)
91
+ ```
92
+ This leaves you with a valid FITS header
93
+
@@ -0,0 +1,65 @@
1
+ # fits2tmp
2
+
3
+ This project allows you to take a large FITS file and render it as a set of smaller FITS files stored in-memory in a temporary directory, with appropriately updated headers.
4
+ The program returns a list of the location of these files, which can then be processed, by other programs,perhaps by a parallel processing framework such as Dask, or by using Python's `subprocess` module to allow feeding those files to a non-Python program with a command line interface (CLI).
5
+
6
+ The program also allows you to turn a zarr file containing your FITS data into small FITS files, provided you have extracted the FITS file header to a text file.
7
+
8
+ This can also be done for hdf5 files (again assuming FITS file header data is saved to a text file). Note that if you attempt to do this with Dask running on multiple nodes, it will currently fail, because of a bug in Dask.
9
+
10
+ Utilities in the program can be used to write headers out to or read them from a text file.
11
+
12
+ ## TODO:
13
+
14
+ - Add a utility to convert FITS files to zarr, including saving the header out to a text file.
15
+ - Add hdf5 support, using more native hdf5 metadata storage solutions.
16
+ - Add xradio/xarray support.
17
+
18
+ ## Installing fits2tmp
19
+
20
+ The program is available from pypi so can be installed with pip: `pip install fits2tmp`.
21
+
22
+ ## Example Usage
23
+
24
+ ```
25
+ import fits2tmp as f2t
26
+
27
+ a = f2t.fitstmpfiles("fits", "/path/to/input/data", "/path/to/temporary/filesystem", "path/to/header/", (100, 100, 100), chunks=(20,20,20))
28
+ ```
29
+ fitstmpfiles takes a large input data set in one of the supported formats, and returns smaller FITS files to a specified directory.
30
+
31
+ The parameters for fitstmpfiles are:
32
+
33
+ - filetype: a string describing the filetype; currently supports "fits" and "zarr" fully; "hdf5" only supported on single node runs.
34
+ - inputpath: path to the input data
35
+ - destination: path to destination directory. It is intended that temporary directories are used, to try to keep the data in-memory, rather than doing potentially expensive disk writes; however, if you wish to write to more permanent storage, that is possible. You will probably want to clean up manually once the run is done, though.
36
+ - headerloc: defaults to `None`. If loading from a FITS file, this isn't required; if you are loading from zarr or hdf5, you need to provide the path to a text file containing the FITS headers. It is recommended that you use the fits2tmp utilities to prepare the header file.
37
+ - chunks: defaults to `None`. You will want to set this to some value that makes sense for the resources on which you're processing the data. Dask recommend that you have at least 10 times as much RAM as the size of the chunk you're processing. You will also want to consider how much overlap you are using when setting the chunk size. If you are particularly concerned about file loading performance, then consider setting the chunk size to a multiple of the block size used in the storage.
38
+ - overlap: defaults to `None`. You will want to set this to some size that makes sense for the data processing you're doing. You probably want to set this to approximately the size of the largest Gaussian kernel you plan to use to detect objects/features.
39
+
40
+ ## Utilities
41
+
42
+ These small methods are provided to make it easy to write out a FITS header to a file, to allow provision of FITS metadata when using other storage solutions.
43
+ To create a header from an existing FITS file:
44
+
45
+ ```
46
+ from astropy.io import fits
47
+ import fits2tmp as f2t
48
+
49
+ with fits.open('path/to/fits/file') as f:
50
+ header = f[0].header
51
+ d = f2t.header_dict(header)
52
+ f2t.dict2file(d, 'path/to/where/you/save/header')
53
+ ```
54
+
55
+ Note that this process strips most of the history from the header; if you wish to update the history, use some of the astropy.io.fits tools to accomplish this. This tool is purely to provide a minimal amount of metadata to allow storing FITS data in zarr and similar file formats, and also to allow processing of sections of FITS files where it's not desirable to have all processes reading from the same file in storage.
56
+
57
+ To create a header from text file containing a json dump of a dictionary created from a FITS header:
58
+
59
+ ```
60
+ import fits2tmp as ft2
61
+
62
+ loaded_header_dict = f2t.file2dict('/path/to/header/txt/file')
63
+ header = dict2header(loaded_header_dict)
64
+ ```
65
+ This leaves you with a valid FITS header
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "fits2tmp"
3
- version = "0.2.0"
3
+ version = "0.4.0"
4
4
  description = "A way of loading FITS files into temporary filesystems to make them available in memory."
5
5
  license = "BSD-3-Clause"
6
6
  authors = ["Verity Allan <vla22@cam.ac.uk>"]
@@ -15,6 +15,10 @@ h5py = "^3.12.1"
15
15
  numpy = "^2.1.2"
16
16
  pytest = "^8.3.3"
17
17
  zarr = "^2.18.3"
18
+ fsspec = "^2026.2.0"
19
+ requests = "^2.32.5"
20
+ aiohttp = "^3.13.3"
21
+ s3fs = "^2026.2.0"
18
22
 
19
23
  [tool.pytest.ini_options]
20
24
  addopts = [
@@ -0,0 +1,192 @@
1
+ import h5py
2
+ import zarr
3
+ import json
4
+ from astropy.io import fits
5
+ from astropy import wcs
6
+ import dask.array as da
7
+ from dask import delayed
8
+ from dask.distributed import Lock
9
+ import numpy as np
10
+ import os
11
+ import dask
12
+ import tempfile
13
+
14
+ def arr_size_check(source_arr, desired_arr: tuple) -> bool:
15
+ if len(source_arr.shape) < len(desired_arr):
16
+ raise ValueError("You've asked for an array with more dimensions than the array stored")
17
+ else:
18
+ for i in range(min(len(source_arr.shape), len(desired_arr))):
19
+ # using min here allows us to avoid an IndexError when we have more dimensions in the source array
20
+ j = source_arr.shape[i] - desired_arr[i]
21
+ if j < 0:
22
+ raise IndexError("Desired array size is larger than the stored array in at least position %s: source array has dimensions %s, and you have requested $s", i, source_arr.shape, desired_arr)
23
+ return True
24
+
25
+ #def fits_add_header(orig_header, sliced_data) -> fits.Header:
26
+ # coords = wcs.WCS(orig_header)
27
+ # new_header = orig_header
28
+ # new_header.update(coords[data_sli].to_header())
29
+ # return new_header
30
+
31
+ def header_dict(header) -> dict:
32
+ d = {}
33
+ for i in header.cards:
34
+ d[i.keyword] = i.value
35
+ return d
36
+
37
+ def dict2header(input_dict: dict) -> fits.Header:
38
+ nh = fits.Header(cards=input_dict)
39
+ return nh
40
+
41
+ def dict2file(input_dict: dict, filepath):
42
+ with open(filepath, 'w') as f:
43
+ f.write(json.dumps(input_dict))
44
+
45
+ def file2dict(filepath) -> dict:
46
+ with open(filepath, 'r') as j:
47
+ contents = json.loads(j.read())
48
+ return contents
49
+ filepath = []
50
+
51
+ def disk2dah(filetype, inputpath, destination, headerloc, arraysize, chunks=None, use_fsspec=False, storage_opts=None):
52
+ # TODO: ADD code to handle arraysize differing from size of array in file
53
+ # maybe extend arr_size_check()?
54
+ # TODO make this support arrays that have other than 3 dims
55
+ destination = tempfile.TemporaryDirectory(dir=destination, delete=False)
56
+ match filetype:
57
+ case "fits":
58
+ with fits.open(inputpath, use_fsspec=use_fsspec, fsspec_kwargs=storage_opts) as f:
59
+ match len(arraysize):
60
+ case 1:
61
+ if len(arraysize) == len(f[0].data.shape):
62
+ d = da.from_array(f[0].data[0:arraysize[0]], chunks=chunks)
63
+ elif len(arraysize) == len(f[0].data.shape) - 1:
64
+ d = da.from_array(f[0].data[0:arraysize[0],0], chunks=chunks)
65
+ elif len(arraysize) == len(f[0].data.shape) -2:
66
+ d = da.from_array(f[0].data[0:arraysize[0],0,0], chunks=chunks)
67
+ elif len(arraysize) == len(f[0].data.shape) -3:
68
+ d = da.from_array(f[0].data[0:arraysize[0],0,0,0], chunks=chunks)
69
+ else:
70
+ raise ValueError("You've asked to load an array with more than 4 dimensions, which is not supported.")
71
+
72
+ case 2:
73
+ if len(arraysize) == len(f[0].data.shape):
74
+ d = da.from_array(f[0].data[0:arraysize[0], 0:arraysize[1]], chunks=chunks)
75
+ elif len(arraysize) == len(f[0].data.shape) - 1:
76
+ d = da.from_array(f[0].data[0:arraysize[0],0:arraysize[1], 0], chunks=chunks)
77
+ elif len(arraysize) == len(f[0].data.shape) -2:
78
+ d = da.from_array(f[0].data[0:arraysize[0],0:arraysize[1],0,0], chunks=chunks)
79
+ else:
80
+ raise ValueError("You've asked to load an array with more than 4 dimensions, which is not supported.")
81
+ case 3:
82
+ if len(arraysize) == len(f[0].data.shape):
83
+ d = da.from_array(f[0].data[0:arraysize[0], 0:arraysize[1], 0:arraysize[2]], chunks=chunks)
84
+ elif len(arraysize) == len(f[0].data.shape) - 1:
85
+ d = da.from_array(f[0].data[0:arraysize[0],0:arrasize[1], 0:arraysize[2],0], chunks=chunks)
86
+ else:
87
+ raise ValueError("You've asked to load an array with more than 4 dimensions, which is not supported.")
88
+ case 4:
89
+ d = da.from_array(f[0].data[0:arraysize[0], 0:arraysize[1], 0:arraysize[2], 0:arraysize[3]], chunks=chunks)
90
+ n_header = dict2header(header_dict(f[0].header))
91
+ wcs_obj = wcs.WCS(n_header)
92
+ case "zarr":
93
+ if use_fsspec == True:
94
+ d = da.from_zarr(inputpath, storage_options=storage_opts, chunks=chunks)[0:arraysize[0],0:arraysize[1],0:arraysize[2]]
95
+ print(type(d))
96
+ else:
97
+ d = da.from_zarr(inputpath, chunks=chunks)[0:arraysize[0],0:arraysize[1],0:arraysize[2]]
98
+ n_header = dict2header(file2dict(headerloc))
99
+ wcs_obj = wcs.WCS(n_header)
100
+ case "hdf5":
101
+ with h5py.File(filepath, "r") as f:
102
+ #note this is hardcoded for the particular hdf5 files tested - needs parameterisation
103
+ d = da.from_array(f["array"][arraysize], chunks=chunks)
104
+ n_header = dict2header(file2dict(inputpath, headerloc))
105
+ wcs_obj = wcs.WCS(n_header)
106
+
107
+ case "xradio":
108
+ weeble
109
+ case _:
110
+ raise ValueError("The specified file type is not supported by fits2tmp.")
111
+ return [d, destination, n_header, wcs_obj]
112
+
113
+ def fitstmpfiles(filetype, inputpath, destination, arraysize, headerloc=None, chunks=None, overlap=None):
114
+ inputarray = disk2dah(filetype, inputpath, destination, headerloc, arraysize, chunks)
115
+ if overlap == None:
116
+ overlap = [0,0,0]
117
+ darr = inputarray[0]
118
+ destination = inputarray[1]
119
+ header = inputarray[2]
120
+ wcs_obj = inputarray[3]
121
+ # why am I pushing this array to delayed? ans: to parallelise construction of the list
122
+ #dd = darr.to_delayed()
123
+ fpaths = []
124
+ data = []
125
+ for i in range(len(darr.chunks[0])):
126
+ x = i * chunks[0] - overlap[0]
127
+ x_delt = (i* chunks[0]) + chunks[0] + overlap[0]
128
+ # range check - when asking for overlap, we curtail at the edges of the array
129
+ if x < 0:
130
+ x = 0
131
+ if x_delt > arraysize[0] -1:
132
+ x_delt = arraysize[0]
133
+ if len(darr.shape) == 1:
134
+ #can't think of why we want to support 1D array stuff, but it's probably OK to do this, though the headers will end up not very useful, probably
135
+ dest = str(i)
136
+ data = darr[x:x_delt]
137
+ t = wcs_obj[x:x_delt]
138
+ header.update(t.to_header())
139
+ hdu = fits.PrimaryHDU(data=data, header=header)
140
+ hdu.writeto(destination.name + dest + "fits")
141
+ fpaths.append(destination.name + dest + "fits")
142
+ else:
143
+ for j in range(len(darr.chunks[1])):
144
+ y = j * chunks[1] - overlap[1]
145
+ y_delt = (j * chunks[1]) + chunks[1] + overlap[1]
146
+ if y < 0:
147
+ y = 0
148
+ if y_delt > arraysize[1] -1:
149
+ y_delt = arraysize[1]
150
+ if len(darr.shape) == 2:
151
+ dest = str(i) + str(j)
152
+ data = darr[x:x_delt, y:y_delt]
153
+ t = wcs_obj[x:x_delt, y:y_delt]
154
+ header.update(t.to_header())
155
+ hdu = fits.PrimaryHDU(data=data, header=header)
156
+ hdu.writeto(destination.name + dest + "fits")
157
+ fpaths.append(destination.name + dest + "fits")
158
+ elif len(darr.shape) > 2:
159
+ for k in range(len(darr.chunks[2])):
160
+ z = k * chunks[2] - overlap[2]
161
+ z_delt = (k * chunks[2]) + chunks[2] + overlap[2]
162
+ if z < 0:
163
+ z = 0
164
+ if z_delt > arraysize[2] -1:
165
+ z_delt = arraysize[2]
166
+ dest = '-' + str(i) +'-' + str(j) + '-' + str(k)
167
+ if len(darr.shape) == 3:
168
+ data = darr[x:x_delt, y:y_delt, z:z_delt]
169
+ t = wcs_obj[x:x_delt, y:y_delt, z:z_delt]
170
+ header.update(t.to_header())
171
+ elif len(darr.shape) == 4:
172
+ # this assumes we're dealing with radio astronomy data, so we just select all indices in the 4th axis, which is usually polarisation information
173
+ data = darr[x:x_delt, y:y_delt, z:z_delt, :]
174
+ t = wcs_obj[x:x_delt, y:y_delt, z:z_delt, :]
175
+ header.update(t.to_header())
176
+ hdu = fits.PrimaryHDU(data=data, header=header)
177
+ hdu.writeto(destination.name + dest + "fits")
178
+ fpaths.append(destination.name + dest + "fits")
179
+ return fpaths
180
+ #def array2disk(array, metadata, outputtype, destination):
181
+
182
+ #def tmpfitsfile2disk(fitsfile, outputtype, destination):
183
+
184
+ #def tmpfitsfile2array(fitsfile, arraytype, arraysize, chunks=None):
185
+
186
+ #def makezarrmetadata(fitsheader):
187
+
188
+ #def makefitsmetadata(fitsheader, location):
189
+
190
+ #makehdf5metadata
191
+
192
+ #makexradiometadata
fits2tmp-0.2.0/PKG-INFO DELETED
@@ -1,31 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: fits2tmp
3
- Version: 0.2.0
4
- Summary: A way of loading FITS files into temporary filesystems to make them available in memory.
5
- Home-page: https://gitlab.developers.cam.ac.uk/phy/ra/vla22-phd/fits2tmp
6
- License: BSD-3-Clause
7
- Author: Verity Allan
8
- Author-email: vla22@cam.ac.uk
9
- Requires-Python: >=3.10,<4.0
10
- Classifier: License :: OSI Approved :: BSD License
11
- Classifier: Programming Language :: Python :: 3
12
- Classifier: Programming Language :: Python :: 3.10
13
- Classifier: Programming Language :: Python :: 3.11
14
- Requires-Dist: astropy (>=6.1.4,<7.0.0)
15
- Requires-Dist: dask[distributed] (>=2025.1.0,<2026.0.0)
16
- Requires-Dist: h5py (>=3.12.1,<4.0.0)
17
- Requires-Dist: numpy (>=2.1.2,<3.0.0)
18
- Requires-Dist: pytest (>=8.3.3,<9.0.0)
19
- Requires-Dist: zarr (>=2.18.3,<3.0.0)
20
- Project-URL: Repository, https://gitlab.developers.cam.ac.uk/phy/ra/vla22-phd/fits2tmp
21
- Description-Content-Type: text/markdown
22
-
23
- # Sample GitLab Project
24
-
25
- This sample project shows how a project in GitLab looks for demonstration purposes. It contains issues, merge requests and Markdown files in many branches,
26
- named and filled with lorem ipsum.
27
-
28
- You can look around to get an idea how to structure your project and, when done, you can safely delete this project.
29
-
30
- [Learn more about creating GitLab projects.](https://docs.gitlab.com/ee/gitlab-basics/create-project.html)
31
-
fits2tmp-0.2.0/README.md DELETED
@@ -1,8 +0,0 @@
1
- # Sample GitLab Project
2
-
3
- This sample project shows how a project in GitLab looks for demonstration purposes. It contains issues, merge requests and Markdown files in many branches,
4
- named and filled with lorem ipsum.
5
-
6
- You can look around to get an idea how to structure your project and, when done, you can safely delete this project.
7
-
8
- [Learn more about creating GitLab projects.](https://docs.gitlab.com/ee/gitlab-basics/create-project.html)
@@ -1,153 +0,0 @@
1
- import h5py
2
- import zarr
3
- import json
4
- from astropy.io import fits
5
- from astropy import wcs
6
- import dask.array as da
7
- from dask import delayed
8
- from dask.distributed import Lock
9
- import numpy as np
10
- import os
11
- import dask
12
- import tempfile
13
-
14
- def arr_size_check(source_arr, desired_arr: tuple) -> bool:
15
- if len(source_arr.shape) < len(desired_arr):
16
- raise ValueError("You've asked for an array with more dimensions than the array stored")
17
- else:
18
- for i in range(min(len(source_arr.shape), len(desired_arr))):
19
- # using min here allows us to avoid an IndexError when we have more dimensions in the source array
20
- j = source_arr.shape[i] - desired_arr[i]
21
- if j < 0:
22
- raise IndexError("Desired array size is larger than the stored array in at least position %s: source array has dimensions %s, and you have requested $s", i, source_arr.shape, desired_arr)
23
- return True
24
-
25
- #def fits_add_header(orig_header, sliced_data) -> fits.Header:
26
- # coords = wcs.WCS(orig_header)
27
- # new_header = orig_header
28
- # new_header.update(coords[data_sli].to_header())
29
- # return new_header
30
-
31
- def header_dict(header) -> dict:
32
- d = {}
33
- for i in header.cards:
34
- d[i.keyword] = i.value
35
- return d
36
-
37
- def dict2header(input_dict: dict) -> fits.Header:
38
- nh = fits.Header(cards=input_dict)
39
- return nh
40
-
41
- def dict2file(input_dict: dict, filepath):
42
- with open(filepath, 'w') as f:
43
- f.write(json.dumps(input_dict))
44
-
45
- def file2dict(input_file, filepath) -> dict:
46
- with open(filepath, 'r') as j:
47
- contents = json.loads(j.read())
48
- return contents
49
- filepath = []
50
-
51
- def fun(darr, destination, header, block_info=None):
52
- arr_loc = block_info[None]['array-location']
53
- print(arr_loc)
54
- ch_size = block_info[None]['chunk-shape']
55
- chunk_loc = block_info[None]['chunk-location']
56
- coords = []
57
- for i in range(len(arr_loc)):
58
- coords.append(slice(arr_loc[i][0],arr_loc[i][1]))
59
- block_id = str(chunk_loc).replace(',', '-').replace('(', '-').replace(')', '').replace(' ', '')
60
- n_header = dict2header(header_dict(header))
61
- wcs_obj = wcs.WCS(n_header)
62
- n_header.update(wcs_obj[coords].to_header())
63
- out = fits.PrimaryHDU(data=darr, header=n_header)
64
- outpath = destination.name + '/' + block_id + ".fits"
65
- out.writeto(outpath)
66
- lock = Lock()
67
- with lock:
68
- filepath.append(outpath)
69
- return darr
70
-
71
- def fits2tmpfiles(inputpath, destination, chunks):
72
- f = fits.open(inputpath)
73
- destination = tempfile.TemporaryDirectory(dir=destination, delete=False)
74
- d = da.from_array(f[0].data, chunks=chunks)
75
- # TODO overlap
76
- de = da.overlap.overlap(d, depth=None, boundary=None)
77
- da.map_blocks(fun, d, destination, f[0].header, dtype=float).compute()
78
- print(filepath)
79
-
80
- return filepath
81
-
82
- def disk2fakefits(filetype, inputpath, destination, headerloc, arraysize, chunks=None):
83
- # TODO: ADD code to handle arraysize differing from size of array in file
84
- # maybe extend arr_size_check()?
85
- destination = tempfile.TemporaryDirectory(dir=destination, delete=False)
86
- match filetype:
87
- case "fits":
88
- f = fits.open(inputpath)
89
- d = da.from_array(f[0].data[arraysize], chunks=chunks)
90
- n_header = dict2header(header_dict(f[0].header))
91
- wcs_obj = wcs.WCS(n_header)
92
- case "zarr":
93
- d = da.from_zarr(inputpath, chunks=chunks)[arraysize]
94
- n_header = dict2header(file2dict(inputpath, headerloc))
95
- wcs_obj = wcs.WCS(n_header)
96
- case "hdf5":
97
- f = h5py.File(filepath, "r")
98
- #note this is hardcoded for the particular hdf5 files tested - needs parameterisation
99
- d = da.from_array(f["array"][arraysize], chunks=chunks)
100
- n_header = dict2header(file2dict(inputpath, headerloc))
101
- wcs_obj = wcs.WCS(n_header)
102
-
103
- case "xradio":
104
- weeble
105
- case _:
106
- raise ValueError("The specified file type is not supported by fits2tmp.")
107
- return [d, destination, n_header, wcs_obj]
108
-
109
- def fakefits2tmpfiles(filetype, inputpath, destination, headerloc, arraysize, chunks=None):
110
- inputarray = disk2fakefits(filetype, inputpath, destination, headerloc, arraysize, chunks)
111
- darr = inputarray[0]
112
- destination = inputarray[1]
113
- header = inputarray[2]
114
- wcs_obj = inputarray[3]
115
- dd = darr.to_delayed()
116
- fpaths = []
117
- for i in range(len(darr.chunks[0])):
118
- x = i * chunks[0]
119
- x_delt = x + chunks[0] - 1
120
- for j in range(len(darr.chunks[1])):
121
- print(i,j)
122
- y = j * chunks[1]
123
- y_delt = y + chunks[1] - 1
124
- if len(darr.shape) == 2:
125
- dest = str(i) + str(j)
126
- data = dd[i][j]
127
- wcs_coords = wcs_obj.slice((slice(x,x_delt), slice(y,y_delt)))
128
- elif len(darr.shape) > 2:
129
- for k in range(len(darr.chunks[2])):
130
- print(k)
131
- data = dd[i][j][k]
132
- z = k * chunks[2]
133
- z_delt = z + chunks[2] - 1
134
- dest = '-' + str(i) +'-' + str(j) + '-' + str(k)
135
- t = wcs_obj[x:x_delt, y:y_delt, z:z_delt]
136
- header.update(t.to_header())
137
- hdu = fits.PrimaryHDU(data=data.compute(), header=header)
138
- hdu.writeto(destination.name + dest + "fits")
139
- fpaths.append(destination.name + dest + "fits")
140
- return fpaths
141
- #def array2disk(array, metadata, outputtype, destination):
142
-
143
- #def tmpfitsfile2disk(fitsfile, outputtype, destination):
144
-
145
- #def tmpfitsfile2array(fitsfile, arraytype, arraysize, chunks=None):
146
-
147
- #def makezarrmetadata(fitsheader):
148
-
149
- #def makefitsmetadata(fitsheader, location):
150
-
151
- #makehdf5metadata
152
-
153
- #makexradiometadata
File without changes