Skip to content

Commit 393d273

Browse files
committed
Merge branch 'main' into feature/jax_merge
2 parents 42f688c + 60ee228 commit 393d273

File tree

7 files changed

+170
-54
lines changed

7 files changed

+170
-54
lines changed

autoarray/config/visualize/plots.yaml

Lines changed: 1 addition & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,11 @@
55

66
dataset: # Settings for plots of all datasets (e.g. ImagingPlotter, InterferometerPlotter).
77
subplot_dataset: true # Plot subplot containing all dataset quantities (e.g. the data, noise-map, etc.)?
8-
data: false # Plot the individual data of every dataset?
9-
noise_map: false # Plot the individual noise-map of every dataset?
10-
signal_to_noise_map: false # Plot the individual signal-to-noise-map of every dataset?
11-
over_sample_size_lp: false # Plot the over-sampling size, used to evaluate light profiles, of every dataset?
12-
over_sample_size_pixelization: false # Plot the over-sampling size, used to evaluate pixelizations, of every dataset?
138
imaging: # Settings for plots of imaging datasets (e.g. ImagingPlotter)
149
psf: false
1510
fit: # Settings for plots of all fits (e.g. FitImagingPlotter, FitInterferometerPlotter).
1611
subplot_fit: true # Plot subplot of all fit quantities for any dataset (e.g. the model data, residual-map, etc.)?
1712
subplot_fit_log10: true # Plot subplot of all fit quantities for any dataset using log10 color maps (e.g. the model data, residual-map, etc.)?
18-
all_at_end_png: true # Plot all individual plots listed below as .png (even if False)?
19-
all_at_end_fits: true # Plot all individual plots listed below as .fits (even if False)?
20-
all_at_end_pdf: false # Plot all individual plots listed below as publication-quality .pdf (even if False)?
2113
data: false # Plot individual plots of the data?
2214
noise_map: false # Plot individual plots of the noise-map?
2315
signal_to_noise_map: false # Plot individual plots of the signal-to-noise-map?
@@ -30,9 +22,6 @@ fit_imaging: {} # Settings for plots of fits to imagi
3022
inversion: # Settings for plots of inversions (e.g. InversionPlotter).
3123
subplot_inversion: true # Plot subplot of all quantities in each inversion (e.g. reconstrucuted image, reconstruction)?
3224
subplot_mappings: true # Plot subplot of the image-to-source pixels mappings of each pixelization?
33-
all_at_end_png: true # Plot all individual plots listed below as .png (even if False)?
34-
all_at_end_fits: true # Plot all individual plots listed below as .fits (even if False)?
35-
all_at_end_pdf: false # Plot all individual plots listed below as publication-quality .pdf (even if False)?
3625
data_subtracted: false # Plot individual plots of the data with the other inversion linear objects subtracted?
3726
reconstruction_noise_map: false # Plot image of the noise of every mesh-pixel reconstructed value?
3827
sub_pixels_per_image_pixels: false # Plot the number of sub pixels per masked data pixels?
@@ -41,22 +30,6 @@ inversion: # Settings for plots of inversions (e
4130
reconstructed_image: false # Plot image of the reconstructed data (e.g. in the image-plane)?
4231
reconstruction: false # Plot the reconstructed inversion (e.g. the pixelization's mesh in the source-plane)?
4332
regularization_weights: false # Plot the effective regularization weight of every inversion mesh pixel?
44-
interferometer: # Settings for plots of interferometer datasets (e.g. InterferometerPlotter).
45-
amplitudes_vs_uv_distances: false
46-
phases_vs_uv_distances: false
47-
uv_wavelengths: false
48-
dirty_image: false
49-
dirty_noise_map: false
50-
dirty_signal_to_noise_map: false
5133
fit_interferometer: # Settings for plots of fits to interferometer datasets (e.g. FitInterferometerPlotter).
5234
subplot_fit_dirty_images: false # Plot subplot of the dirty-images of all interferometer datasets?
53-
subplot_fit_real_space: false # Plot subplot of the real-space images of all interferometer datasets?
54-
amplitudes_vs_uv_distances: false
55-
phases_vs_uv_distances: false
56-
uv_wavelengths: false
57-
dirty_image: false
58-
dirty_noise_map: false
59-
dirty_signal_to_noise_map: false
60-
dirty_residual_map: false
61-
dirty_normalized_residual_map: false
62-
dirty_chi_squared_map: false
35+
subplot_fit_real_space: false # Plot subplot of the real-space images of all interferometer datasets?

autoarray/inversion/inversion/mapper_valued.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ def magnification_via_mesh_from(
259259

260260
def magnification_via_interpolation_from(
261261
self,
262-
shape_native: Tuple[int, int] = (401, 401),
262+
shape_native: Tuple[int, int] = (201, 201),
263263
extent: Optional[Tuple[float, float, float, float]] = None,
264264
) -> float:
265265
"""

autoarray/operators/transformer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def pylops_exception():
4747
"\n--------------------\n"
4848
"You are attempting to perform interferometer analysis.\n\n"
4949
"However, the optional library PyLops (https://github.com/PyLops/pylops) is not installed.\n\n"
50-
"Install it via the command `pip install pylops==1.18.3`.\n\n"
50+
"Install it via the command `pip install pylops==2.3.1`.\n\n"
5151
"----------------------"
5252
)
5353

autoarray/plot/multi_plotters.py

Lines changed: 95 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import os
2+
from pathlib import Path
13
from typing import List, Optional, Tuple
24

35
from autoarray.plot.wrap.base.ticks import YTicks
@@ -104,29 +106,6 @@ def plot_via_func(self, plotter, figure_name: str, func_name: str, kwargs):
104106
else:
105107
func(**{**{figure_name: True}, **kwargs})
106108

107-
def output_subplot(self, filename_suffix: str = ""):
108-
"""
109-
Outplot the subplot to a file after all figures have been plotted on the subplot.
110-
111-
The multi-plotter requires its own output function to ensure that the subplot is output to a file, which
112-
this provides.
113-
114-
Parameters
115-
----------
116-
filename_suffix
117-
The suffix of the filename that the subplot is output to.
118-
"""
119-
120-
if self.plotter_list[0].mat_plot_1d is not None:
121-
self.plotter_list[0].mat_plot_1d.output.subplot_to_figure(
122-
auto_filename=f"subplot_{filename_suffix}"
123-
)
124-
if self.plotter_list[0].mat_plot_2d is not None:
125-
self.plotter_list[0].mat_plot_2d.output.subplot_to_figure(
126-
auto_filename=f"subplot_{filename_suffix}"
127-
)
128-
self.plotter_list[0].close_subplot_figure()
129-
130109
def subplot_of_figure(
131110
self, func_name: str, figure_name: str, filename_suffix: str = "", **kwargs
132111
):
@@ -266,6 +245,99 @@ def subplot_of_multi_yx_1d(self, filename_suffix="", **kwargs):
266245
)
267246
self.plotter_list[0].plotter_list[0].close_subplot_figure()
268247

248+
def output_subplot(self, filename_suffix: str = ""):
249+
"""
250+
Outplot the subplot to a file after all figures have been plotted on the subplot.
251+
252+
The multi-plotter requires its own output function to ensure that the subplot is output to a file, which
253+
this provides.
254+
255+
Parameters
256+
----------
257+
filename_suffix
258+
The suffix of the filename that the subplot is output to.
259+
"""
260+
261+
plotter = self.plotter_list[0]
262+
263+
if plotter.mat_plot_1d is not None:
264+
plotter.mat_plot_1d.output.subplot_to_figure(
265+
auto_filename=f"subplot_{filename_suffix}"
266+
)
267+
if plotter.mat_plot_2d is not None:
268+
plotter.mat_plot_2d.output.subplot_to_figure(
269+
auto_filename=f"subplot_{filename_suffix}"
270+
)
271+
plotter.close_subplot_figure()
272+
273+
def output_to_fits(
274+
self,
275+
func_name_list: List[str],
276+
figure_name_list: List[str],
277+
filename: str,
278+
tag_list: Optional[List[str]] = None,
279+
remove_fits_first: bool = False,
280+
**kwargs,
281+
):
282+
"""
283+
Outputs a list of figures of the plotter objects in the `plotter_list` to a single .fits file.
284+
285+
This function takes as input lists of function names and figure names and then calls them via
286+
the `plotter_list` with an interface that outputs each to a .fits file.
287+
288+
For example, if you have multiple `ImagingPlotter` objects and want to output the `data` and `noise_map` of
289+
each to a single .fits files, you would input:
290+
291+
- `func_name_list=['figures_2d', 'figures_2d']` and
292+
- `figure_name_list=['data', 'noise_map']`.
293+
294+
The implementation of this code is hacky, with it using a specific interface in the `Output` object
295+
which sets the format to `fits_multi` to call a function which outputs the .fits files. A major visualuzation
296+
refactor is required to make this more elegant.
297+
298+
Parameters
299+
----------
300+
func_name_list
301+
The list of function names that are called to plot the figures on the subplot.
302+
figure_name_list
303+
The list of figure names that are plotted on the subplot.
304+
filenane
305+
The filename that the .fits file is output to.
306+
tag_list
307+
The list of tags that are used to set the `EXTNAME` of each hdu of the .fits file.
308+
remove_fits_first
309+
If the .fits file already exists, it is removed before the new .fits file is output, else it is updated
310+
with the figure going into the next hdu.
311+
kwargs
312+
Any additional keyword arguments that are passed to the function that plots the figure on the subplot.
313+
"""
314+
315+
output_path = self.plotter_list[0].mat_plot_2d.output.output_path_from(
316+
format="fits_multi"
317+
)
318+
output_fits_file = Path(output_path)/ f"{filename}.fits"
319+
320+
if remove_fits_first:
321+
output_fits_file.unlink(missing_ok=True)
322+
323+
for i, plotter in enumerate(self.plotter_list):
324+
plotter.mat_plot_2d.output._format = "fits_multi"
325+
326+
plotter.set_filename(filename=f"{filename}")
327+
328+
for j, (func_name, figure_name) in enumerate(
329+
zip(func_name_list, figure_name_list)
330+
):
331+
if tag_list is not None:
332+
plotter.mat_plot_2d.output._tag_fits_multi = tag_list[j]
333+
334+
self.plot_via_func(
335+
plotter=plotter,
336+
figure_name=figure_name,
337+
func_name=func_name,
338+
kwargs=kwargs,
339+
)
340+
269341

270342
class MultiYX1DPlotter:
271343
def __init__(

autoarray/plot/wrap/base/output.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ def __init__(
6161
self.format_folder = format_folder
6262
self.bypass = bypass
6363
self.bbox_inches = bbox_inches
64+
self._tag_fits_multi = None
6465

6566
self.kwargs = kwargs
6667

@@ -105,6 +106,7 @@ def savefig(self, filename: str, output_path: str, format: str):
105106
plt.savefig(
106107
path.join(output_path, f"{filename}.{format}"),
107108
bbox_inches=self.bbox_inches,
109+
pad_inches=0,
108110
)
109111
except ValueError as e:
110112
logger.info(
@@ -151,6 +153,17 @@ def to_figure(
151153
file_path=path.join(output_path, f"{filename}.fits"),
152154
overwrite=True,
153155
)
156+
elif format == "fits_multi":
157+
if structure is not None:
158+
from autoarray.structures.arrays.array_2d_util import (
159+
update_fits_file,
160+
)
161+
162+
update_fits_file(
163+
arr=structure.native,
164+
file_path=path.join(output_path, f"{filename}.fits"),
165+
tag=self._tag_fits_multi,
166+
)
154167

155168
def subplot_to_figure(self, auto_filename: Optional[str] = None):
156169
"""

autoarray/plot/wrap/two_d/voronoi_drawer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ def draw_voronoi_pixels(
8181

8282
cmap = plt.get_cmap(cmap.cmap)
8383

84-
if colorbar is not None:
84+
if colorbar is not None and colorbar is not False:
8585
cb = colorbar.set_with_color_values(
8686
units=units,
8787
norm=norm,

autoarray/structures/arrays/array_2d_util.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -913,3 +913,61 @@ def header_obj_from(file_path: Union[Path, str], hdu: int) -> Dict:
913913
hdu_list = fits.open(file_path)
914914

915915
return hdu_list[hdu].header
916+
917+
918+
def update_fits_file(
919+
arr: np.ndarray,
920+
file_path: str,
921+
tag: Optional[str] = None,
922+
header: Optional[fits.Header] = None,
923+
):
924+
"""
925+
Update a .fits file with a new array.
926+
927+
This function is used by the `fits_multi` output interface so that a single .fits file with groups of data
928+
in hdu's can be created.
929+
930+
It may receive a `tag` which is used to set the `EXTNAME` of the HDU in the .fits file and therefore is the name
931+
of the hdu seen by the user when they open it with DS9 or other .fits software.
932+
933+
A header may also be provided, which by default has the pixel scales of the array added to it.
934+
935+
Parameters
936+
----------
937+
arr
938+
The array that is written to the .fits file.
939+
file_path
940+
The full path of the file that is output, including the file name and ``.fits`` extension.
941+
tag
942+
The `EXTNAME` of the HDU in the .fits file.
943+
header
944+
The header of the .fits file that the array is written to, which if blank will still contain the pixel scales
945+
of the array.
946+
"""
947+
948+
if header is None:
949+
header = fits.Header()
950+
951+
try:
952+
y, x = map(str, arr.pixel_scales)
953+
header["PIXSCAY"] = y
954+
header["PIXSCAX"] = x
955+
except AttributeError:
956+
pass
957+
958+
if conf.instance["general"]["fits"]["flip_for_ds9"]:
959+
arr = np.flipud(arr)
960+
961+
if os.path.exists(file_path):
962+
with fits.open(file_path, mode="update") as hdul:
963+
hdul.append(fits.ImageHDU(arr, header))
964+
if tag is not None:
965+
hdul[-1].header["EXTNAME"] = tag.upper()
966+
hdul.flush()
967+
968+
else:
969+
hdu = fits.PrimaryHDU(arr, header)
970+
if tag is not None:
971+
hdu.header["EXTNAME"] = tag.upper()
972+
hdul = fits.HDUList([hdu])
973+
hdul.writeto(file_path, overwrite=True)

0 commit comments

Comments
 (0)