Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Some plotting and representation improvements #260

Merged
merged 6 commits into from Aug 9, 2023

Conversation

teutoburg
Copy link
Contributor

  1. Some improvements to plotting methods of SpectralTrace, SpectralTraceList and DetectorList. They now use matplotlib's "OOP mode" (i.e. fig, ax instead of all plt.whatever), as a first step to migrate all plotting related functions to that. This allows e.g. to easily plot the spectral traces into the detector layout plot by passing the axes object (this use-case was the main motivation for this modification). More configurability for the plot methods was added along the way, but the default behavior should remain the same as before.
  2. A _repr_pretty_ method was added to some classes, which is accessed by IPython to support a nicer human-readable representation of an object. This is currently very experimental, as there are multiple (not really compatible) ways to implement this. This will require more though, experimentation, and finally standardization. And probably a separate issue...

@codecov
Copy link

codecov bot commented Aug 4, 2023

Codecov Report

Patch coverage: 28.24% and project coverage change: -0.30% ⚠️

Comparison is base (c700bc4) 80.85% compared to head (331e12a) 80.55%.

Additional details and impacted files
@@              Coverage Diff               @@
##           dev_master     #260      +/-   ##
==============================================
- Coverage       80.85%   80.55%   -0.30%     
==============================================
  Files             143      143              
  Lines           14676    14736      +60     
==============================================
+ Hits            11866    11871       +5     
- Misses           2810     2865      +55     
Files Changed Coverage Δ
scopesim/optics/optical_train.py 43.84% <3.70%> (-4.36%) ⬇️
scopesim/effects/spectral_trace_list_utils.py 59.56% <11.11%> (-3.28%) ⬇️
scopesim/optics/optical_element.py 61.78% <25.00%> (-1.24%) ⬇️
scopesim/detector/detector_array.py 86.00% <33.33%> (-11.57%) ⬇️
scopesim/effects/spectral_trace_list.py 53.61% <52.94%> (+0.91%) ⬆️
scopesim/effects/effects.py 90.58% <70.00%> (+2.08%) ⬆️
scopesim/effects/detector_list.py 92.70% <100.00%> (-0.06%) ⬇️

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@teutoburg teutoburg marked this pull request as ready for review August 5, 2023 00:52
if len(Gcf.figs) == 0:
plt.figure(figsize=(12, 12))
if axes is None:
fig, axes = plt.subplots(figsize=(12, 12))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we keep the absolute figsize or should we leave that to the user? While 12in x 12in makes a nice LP cover, it is a tad too large for my computer screens.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know what the original motivation was for this giant plot, but I thought there might have been a good reason, so I left it in. That said, working on the .plot() methods here motivated me to go over those in general for other classes (PR coming soon!) and the figure creation will be "outsourced" to a new function utils.figure_factory anyway, so the explicit figsize here will most likely go away as a result of that...

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Giant plots are needed to see where the spectral traces are actually positioned on the detector plane. However this should only be a default if someone hasn't already generated a figure or passed an Axes object.
Feel free to change this to something else if it's bothersome 👍

hdu = self[trace_id].rectify(hdulist,
interps=interps,
bin_width=bin_width,
xi_min=xi_min, xi_max=xi_max,
wave_min=wave_min, wave_max=wave_max)
if hdu is not None: # ..todo: rectify does not do that yet
outhdul.append(hdu)
outhdul[0].header[f"EXTNAME{i+1}"] = trace_id
outhdul[0].header[f"EXTNAME{i}"] = trace_id
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i is in the range [0, len(spec traces)] from the enumerate call in line 302
The primary header EXTNAME0 will (should) never contain trace data.
Trace data should only be held in the additional FITS extensions 1..N, i.e. EXTNAME[1 ... len(spec traces)]

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you look a few lines further up, I also changed the enumerate to enumerate(self.spectral_traces, start=1), which means i is in range(1, len(self.spectral_traces) + 1). This (in general, not only in this example) has the benefits of more clearly marking the purpose of i (we want values from 1, not 0) and saving to type +1 every time (which may cause bugs if forgotten once). Of course, this only makes sense if i is only used as i+1 within that particular scope (which is the case here).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Today I learned ... :)

aperture_id = self.meta["aperture_id"]
lam_arr = self.table[self.meta["wave_colname"]]

wave_max = np.max(lam_arr)
wave_min = np.min(lam_arr)

return {"wave_min": wave_min, "wave_max": wave_max,
"trace_id": trace_id, "aperture_id": aperture_id}
"trace_id": self.trace_id, "aperture_id": aperture_id}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is self.trace_id first defined? I can't find it in __init__

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or is this one of those new-fangled things that _class_param dict entries can be accessed through self.?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a property that already existed anyway and is defined in (new) line 599. It should probably be a bit further up, it's somewhat "hidden in the jungle" now. I wrote that in my general list and will include it later.

Copy link
Collaborator

@astronomyk astronomyk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apart from the question I have regarding the use of self.trace_id when this is not explicitly available from self.or in the_class_paramsdict, and the change back toEXTNAME{i+1}`, it looks good to go.

Good job!

aperture_id = self.meta["aperture_id"]
lam_arr = self.table[self.meta["wave_colname"]]

wave_max = np.max(lam_arr)
wave_min = np.min(lam_arr)

return {"wave_min": wave_min, "wave_max": wave_max,
"trace_id": trace_id, "aperture_id": aperture_id}
"trace_id": self.trace_id, "aperture_id": aperture_id}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or is this one of those new-fangled things that _class_param dict entries can be accessed through self.?

@@ -207,7 +206,7 @@ def map_spectra_to_focal_plane(self, fov):
xilam = XiLamImage(fov, self.dlam_per_pix)
self._xilamimg = xilam # ..todo: remove or make available with a debug flag?
except ValueError:
print(f" ---> {self.meta['trace_id']} gave ValueError")
print(f" ---> {self.trace_id} gave ValueError")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as comment above

@@ -548,7 +624,7 @@ def __repr__(self):
return f"{self.__class__.__name__}({self.table!r}, **{self.meta!r})"

def __str__(self):
msg = (f"<SpectralTrace> \"{self.meta['trace_id']}\" : "
msg = (f"<SpectralTrace> \"{self.trace_id}\" : "
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above with self.trace_id

@teutoburg teutoburg merged commit 28dc5dc into AstarVienna:dev_master Aug 9, 2023
17 of 18 checks passed
@teutoburg teutoburg deleted the fh/plots-and-reprs branch August 9, 2023 22:44
@teutoburg teutoburg mentioned this pull request Aug 23, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants