FinalReport HERMES OTA Project2

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 44

The Hermes Spectrograph

Data Analysis from the Observations of 16 August 2020

David C. Petit
r0818065

Project #2 for
Observational Techniques in Astronomy

Institute of Astronomy
KU Leuven
Belgium
Autumn 2020
Contents
1 Introduction 1

2 Blaze Wavelength 2

3 Blaze Angle 4

4 Spectral Resolution and Sampling 7


4.1 Resolution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
4.2 Sampling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

5 Spectral Range 11

6 Stability 12

7 Efficiency 17

8 Discussion 18
8.1 Error Analysis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
8.2 Future Works and Investigations . . . . . . . . . . . . . . . . . . . . 21

9 Conclusion 21

10 References 22

11 Appendix A: Plots, Figures, and Graphs 23

12 Appendix B: Programming Codes 33

i
Abstract
A night of observations by the HERMES Spectrograph is analyzed and presented in
this report. Consideration is given to many aspects of the spectrograph, including:
flat-field spectrum formation, Blaze wavelength, blaze angle of every order, spectral
resolution and sampling, free spectral range, velocity stability, and efficiency of the
whole system with a comparison using an Exposure Time Calculator. After the
comparative analysis of each of its components theoretical to its calculated or
measured, the system and its one day of operation are generalized and discussed
as a whole.

1 Introduction
The HERMES, or High Efficiency and Resolution Mercator Echelle Spectrograph
[1], is the spectrograph of the Mercator telescope on La Palma [2]. Though this
optical telescope’s mirror diameter is 1.2 m, which is approximately one eighth
of the Very Large Telescope and one fourtieth of the planned Extremely Large
Telescope, it is particularly useful for the creations of high-quality time series of
high-resolution spectra. In fact its smaller size help contribute to its resolution.
Unlike published materials, throughout this paper, (terminal) commands, as well
as equations, are isolated and centered for clarity and ease of finding while working
on future projects.

A diagram of Hermes, seen in figure 1, shows the path of light in the instrument.
A selective summary can describe the light as spreading out from a source onto
a collimator, which curved shape reflects the light so that the rays are in parallel
en route to the echelle grating; this separates the light into the many colors of the
rainbow we are all so familiar with as well as some near IR and UV light. These
bundles of light at their different wavelengths then undergo a few more changes
as they bounce off and bend through the second collimator, prisms, and a camera
before finally reaching the charge-coupled device (CCD). Here the photons are
converted into electrical signal and a readout of the source’s emissions are made
in a computer.

The goals of this project are to become familiar with data from high-resolution
spectra, calibrate and merge the spectra over the different orders within the
spectrograph, understand the relationship between optical manufacturing designs
and its corresponding data, and learn about and calculate the stability and effi-
ciency of our whole system. These goals have been accomplished by analyzing a
night’s worth of data, namely on 16 August 2020 (frequently written as 20200816

1
Figure 1: The High Efficiency and Resolution Mercator Echelle Spectrograph[2]

throughout the code of this report), just before the beginning of this remarkable
Coronavirus-hampered semester. Allocated for the purpose of this project were 8
files on Toledo for all students and then one folder of data from the night. This
folder contained four sub-folders, including the raw measurement data, and the
reduced data. Calibration data was located in here as well. This project relied
heavily on many of the ∼175 reduced (and calibration) data files.

The data in this project is primarily found in Flexible Image Transport System
(FITS) files. Fits files were developed for and are the gold standard in the field of
astronomy and describing photometric and spatial calibration information, along
with the metadata on the image’s creation [3]. The HRF, LRF, and WRF terms
in the file names refer to the High and Low-Resolution and Wavelength Reference
Fibers that convey the light into the spectrograph. Several other other, more
commonplace file types, like .py, .png, etc. were also used to understand high
resolution spectra data.

2 Blaze Wavelength
High resolution spectra data analysis requires a working familiarity with vast quan-
tities and different types of data files and computer language. In determining the

2
blaze wavelength, I copied the files “00972537 HRF FF ext.fits,” “00972557 HRF TH ext.fits,”
and “00972557 HRF TH ext wavelengthScale.fits” along with the other data files
from the cluster at to my computer with with the command,

scp -r davidp@copernicus.ster.kuleuven.be:/STER/mercator/hermes/20200816/reduced/ /home/

I attempted to run PlotSpectrumHermes.py from Toledo on my computer as, $


python PlotSpectrumHermes.py -i nnnnnnn -n 20200816 -w 6500 this threw error
messages. Believing the 7 n’s to be the 8 digits at the start of the reduced data
files’ names, I substituted for the n’s and moved on. After copying it from my
computer to Yildun in the IvS cluster, I tried running it there. It failed because
I had not sourced the pipleline and initialized python; these were accomplished
with, $ source /STER/mercator/mercator/HermesDRS.rc and $ ml anaconda a
new error message involving a Traceback was thrown. Testing the procedure on
other nights and with dropping the “00” in front of the nnnnnnn variable yielded
better results. Namely, with the command,

$pythonP lotSpectrumHermes.py − i 972571 − n 20200816 − w 6500

a plot was obtained and shown here in figure 2. The source of the error was then
believed to come from the original n value file equaling 972557 not having a corre-
sponding CosmicsRemoved log merged cf.fits file. The lesson learned was to only
run PlotSpectrumHermes.py with the n valuesthat had corresponding CosmicsRe-
moved files. Much knowledge of fits files and spectra data was obtained in this
project.
Generating plots like the one in figure 2, which are centered on a chosen wavelength
(i.e. 6500 A) and having a range of plus minus 10 A proved to be remarkably
tedious and uninsightful. Eventually there was a notiching of the icon showing an
arrow staggering up between an x and y axis. This proved to be very useful in
zooming out on any of the IvS cluster generated plot.
Sequential plots over what will be deemed as all wavelengths were made from 3600
A to 9200 A, using several different n values. An example of such code is seen
here,

$pythonP lotSpectrumHermes.py − i 972571 − n20200816 − w 6000

$pythonP lotSpectrumHermes.py − i 972572 − n20200816 − w 6000


Each of the fits files from the night assigned have data given in pixels that corre-
spond to wavelengths. There are 55 orders in the spectrographic data and each
order has 4608 pixels, which span a range of approximately 212 A. A back-of-the-
envelope calculation might motivate you to think that this spectrograph covers

3
Figure 2: Hermes Spectrographic Orders and Intensities

well over 10000 A of the electromagnetic spectrum, but as can be seen in the
graphs, there is substantial overlap of all of the orders as so the entire range covers
approximately 9006 to 3719 A, or 5287A. This is much closer to the visible range
that our eyes are capable of detecting. The orders of Hermes can be seen plotted
in figure 2.

Maximum finding code allowed for the accurate determination of all peak wave-
lengths of the spectrograph. These correspond to the light that reflects most
intensely from the echelle grating, and are known as the Blaze wavelengths. A
table of their values can be found in the appendix.

3 Blaze Angle
Blazed echelle grating are structures that reflect incoming light and because of
their repeated step-wise grooved structure, they separate and spread different
wavelengths of light out like a classic glass prism or rainbow. Intensity of the
light for each lambda is given as,

!2 !2
sin(N πd
λ
(sinβ + sinα)) sin( πd
λ
(sinβ + sinα))
I(α, β) = IF · BF = πd
· πb
N sin( λ (sinβ + sinα)) λ
(sinβ + sinα)

4
It would be quite easy to get lost in the arithmetic, but the simplification that
is most relevant for this section is that BF (the Blaze Function) maximizes the
intensity when α = (−)β [4]. This exact angle is not used because it would result
in pure reflection back into the source, but something very close, actually less than
1 degree, is used to nearly maximize the intensity.

The plot in figure 2 shows the intensities over the 55 orders that cover the spectrum
of wavelengths in the graph between 3600 and 9200 angstrom. The maxima of
these 55 orders are deemed the optimal or blaze-wavelengths. They are tabulated
in the appendix, but can be (poorly linearly) approximated as 3800, 3890, 3980,
. . . 8790, 8880 angstrom. With the accurate wavelengths, the blaze angles can be
derived from,
mλ = d(sin(α) + sin(β))
and knowledge that in Littrow conditions the incoming beam is normal to the
grating facets,
α=β
then calculated with the formula,
m ∗ λB
δ = arcsin
2d
Hermes is in quasi Littrow conditions, so a more complex set of equations are
needed for the most accurate calculations [4],

mλ = d cos γ(sin α + sin β)

or with the appropriate substitutions

mλb = d cos(2 sin δ + cos θ)

where γ is the off-plane angle and θ ∼ 0. These will give rise to a tilt of the
entrance slit on the detector as ξ,

dβ (sin α + sin β)
tan ξ = = tan γ
dγ cos β
Provided a constant γ, ξ can be approximated as constant and,

tan ξ = 2 tan γ tan δ


The γ, rotation off-plane, is very small, 0.8 degrees. This allows redirection of the
outgoing light from the incoming light, but also maintains a near-optimal signal
strength of the incoming light, and maintains an approximation for the simpler ver-
sion of the mathematics used. The term d comes from the echelle grating’s 52.676

5
Figure 3: Blaze wavelengths, measured vs. theorized

grooves per millimeter, which means there are approximately 190,000 angstroms
between each groove. For the increasing m values from 40 to 95, the 55 Blaze
angles were calculated—see table in appendix for complete details.

Summarizing, the average of the 55 angles was 69.66 degrees, the maximum and
minimum were 70.09 and 69.28 degrees respectively. The most impressive thing
to the author was the incredibly small standard deviation around the mean, only
0.198 degrees. Though the calculated angles were not identical to the historical
and theoretical values, they strongly agreed with their approximately 69.7 degree
magnitudes; which is rounded from the information in the lectures on slide 35 as
69.8 degrees, on slide 38 as 69.7 degrees, and on slide 45 as 69.74 degrees. Though
it was not undertaken in this project, it is entirely possible to work in the other
direction, meaning use the approximately constant value of the blaze angle to get
the blaze wavelengths over the orders of 40 to 95 by rewriting the above equation in
terms of λB . Figures 3, 4, and 5 show the calculated blaze wavelengths compared
to the theoretical ones given in the literature [4]. The magnitude of their errors is
shown in the second figure and was made plotting,
errorsquared (λ) = (intensitydata (λ) − intensitydata (λ))2
and their errors are shown directly in the third plot by their differences, that is,
errors(λ) = intensitydata (λ) − intensitydata (λ)

6
Figure 4: Blaze wavelength squared-error magnitudes

The first plot shows how remarkably similar they are, and the second shows that
the error is extremely close to 0 in the middle wavelengths and increases near the
ends. The last plot could be considered the most interesting because it shows a
strong relationship (almost a negative linear one) between the order number and
the error; this suggests that it is a systematic error, instead of a random error,
and could be permanently corrected somewhere along its pipeline.

4 Spectral Resolution and Sampling


4.1 Resolution
The spectral resolution (and related sampling) is the ability for a detector to dis-
tinguish between similar things. Two unrealistically simple examples would be a
light source, which we will call a star, emitting light at two wavelengths, the res-
olution of an instrument is how similar these two wavelengths can be before they
appear as one wavelength to the system. Our eyes can easily discern colors which
are separated by 100s or 10s of nanometers, some resolution powers are much bet-
ter than this; as time goes on and while technology improves we will move towards
the natural limits of resolution that give rise to the Planck-Einstein relation and
the aspects of the modern field of quantum mechanics, all of which are beyond the

7
Figure 5: Blaze wavelength squared-error magnitudes

scope of this project.

In order to derive the resolution, we must know the nature of the instrument given
by,
fcam
w0 = rw = rφDT Fcam
fcol
where w is the width of the entrance slit, DT is the telescope aperture’s diameter,
and φ is the angular sky aperture. Resolution is then thus defined as the optimal
wavelength difference δλ for that allows for image separation between wavelengths
λ and λ + δλ on width of the slit image w0 . The separation between those images
is δ l = fcam Aδλ. In general, this leads to,
λ λAD
R= =
δλ φDT
in grating (and not classical prism style) spectrographs this becomes,

W (sin β + sin α) 2D sin δ cos θ


R= =
φDT φDT cos α
and when θ = 0 and α = δ (in Littrow configuration), this simplifies to,
2Dtanδ
R=
φDT

8
Figure 6: All peaks from calibration for resolution

Extraction for all orders has not occurred due to the generalization of a for loop
over a unique instance not being debugged before time constraints. All of the peaks
over all of the orders were obtained in a single array based on their pixel number;
this information has been tabulated in the appendix. The peaks of a single order
were identified, this can been seen in figure 7. A single peak was discerned and
modeled as a Gaussian distribution that shows high levels of accuracy, and results
in a near-optimal standard deviation of 1.67 pixels as seen in figure 8. The FWHM
of any Gaussian curve is given by,

F W HM = H · 2 2 ln 2
where H is the height of the peak. For the peak thoroughly investigated, this led
to a FWHM of 3800000.

Cursory examinations let to the belief that 1.6 pixels could suffice as the standard
deviation for a large majority of peaks in the calibration emissions. Unfortunately,
peak heights vary immensely and generalizing 3800000 as a typical resolution would
be a poorer approximation. As of submission, the code was still in development
and yielded results that can be seen in the appendix at 19. With time and further
python lessons, the peak heights, H, will also be determined and the FWHM for
all peaks can be obtained leading to a resolution that would fill in the rest of figure

9
Figure 7: The signal peaks identifies in a single order of data

Figure 8: Signal Peak Modeling by Gaussian Distribution

10
20, also found in the appendix.

4.2 Sampling
Sampling is more specifically is given by,
p · Ravg
S=
(λmax –λmin )

where S is the sampling parameter, p is the total number of pixels in the orders.
The math can translate to sampling being the amount of pixels it takes to make
a resolution for the spectrograph, or more precisely, sampling is the number of
pixels per resolution element, which is the FWHM of a non-resolved line from the
section above. This spectrograph in particular is equipped with 4608 pixels, and
from observations a very approximate Ravg value of 4 pixels was obtained. The
range of wavelengths is 5287 A, and was discussed in section 1. Calculating S
yields 3.5 pixels for the average resolution.

5 Spectral Range
The free spectral range is determined by the difference in two similar wavelengths
in an order that can be separated by the echelle grating. If we recall how we began
our analysis on gratings with,

mλ = d(sin(α) + sin(β))

we can see that at a given pair of constants α and β, we can solve for the wave
order m. Then recalling that m is an integer than the value (m+1) must also hold
true for this equation. The free spectral range (FSR) is thus the difference in λ s
that make this equality hold true. Thus we arrive at,

mλ0 = (m + 1)λ

and
λ
F SR = ∆λ = λ0 − λ =
m
This kind of relationship is often best seen graphically. Figure 9 shows the order
wavelengths and their intensities. The values are also tabulated in the appendix
2. Overlap between the orders is clearly visible and the range that each order
holds that does not overlap with its neighboring orders is that order’s free spectral
range. In this range, any photon traveling into that order is guaranteed to belong

11
Figure 9: Free Spectral Ranges

there instead of possibly ending up in a neighboring order ambiguously.

The clarity in the first figure is less than perfect. The following two figure should
provide better viewing for the reader.

The next figure shows the increased spacing between each of the orders’ overlapping
regions. Over any small or even changes in orders, the spacings between the
orders seem to grow linearly. Only over a majority of the total ranges can a non-
linear, possibility quadratic, pattern be observed. It becomes important to prevent
single photons with a wavelength that is covered by two overlapping orders from
registering within the system twice; this can be achieved when the free spectral
range is large with filtering techniques, and when it is short with cross-dispersion
techniques [4]. Both topics are beyond the scope of this project, but I encourage
every reader to pursue their interests with the reference material.

6 Stability
The velocity stability of a night is a measure of how constant the world around the
telescope is over a period of time. Because the Hermes spectrograph is on earth,
the photons traveling the endless miles in the near absolute vacuum of space are

12
Figure 10: Free Spectral Ranges - Low Wavelengths

going to come under the influence of our atmosphere before they enter the spectro-
graph. Clouds, rain, sand, dust, and any of the other “elements” we are immersed
in will have an influence on our observations. These influences are difficult to
measure in their own right, but their aggregate influence can be thought of as a
radial velocity shift. This means that if in an ideal observation (that still had the
non-ideal conditions on earth) where a telescope on earth, a telescope in space,
and a star being observed are all perfectly still (or are all moving with identical
velocity), instead of observing the same energy distribution of photons in the two
telescopes, all of the energy coming into the telescope on earth will be red-shifted
or blue-shifted, like if the star had a radial velocity. Fortunately, mountain tops
of isolated and lowly-populated islands make for minimal disturbances and lead to
generally high stability in astronomical observations.

After reading the cookbook numerous times, some small understanding was gleamed.
The pipleline was initialized and hermesConfig.xml file was successfully edited.
This was accomplished by changing the AnalysesResults and DebugPath from
having “hans” directories to:

<AnalysesResults>/home/davidp/HermesAnalyses/</AnalysesResults>

<DebugP ath>/home/davidp/HermesDebug/</DebugP ath>

13
Figure 11: Free Spectral Ranges - High Wavelengths

Figure 12: Free Spectral Ranges - Spacing Over Orders

14
Figure 13: Stability of the Night - Early (972557)

The entire instructional code can be found in the appendix. Eventually her-
mesVR.py was ran with a command that, for example, looked like,

[davidp@yildun ]$hermesV R−i972572−n20200816−m/ST ER/mercator/mercator/HermesM asks/HermesT hArN eM ask.f its

The relevant summary for here is hermesVR is capable of using the cross-correlation
algorithm with a binary mask, namely ThArNe, which has a large number of avail-
able lines in the spectrum of interest and than thus be an accurate measuring tool
for Doppler shifts (even on noisy data). The program accomplishes this and then
outputs the relevant figures and plots to show how stable a night of observations
is [4]. Many results were generated, but the key figures of calibration checks at
the beginning, middle, and end of the night are included in the body of the report
here 13; the auxiliary figures are located in the appendix.

The user requirements for Hermes on slide 25 indicate that the short-term and
long-term goals for stability are 1 m/s and 5 m/s respectively. A table of radial
velocities, their errors, nnnnnn number, and order taken in the night are seen
below. On average, a vr of 0.003 +/- 0.003 km/s or in simpler units, 3 +/- 3
m/s will almost always meets the user’s requirements in the long-term of 5 m/s,
but can with some degree of frequency fall short of the desired accuracy in the
short-term of 1 m/s. This one night’s observational value also matches well with
the literature value of radial velocity at “∼ 2.5 m · s−1 ” [1].

15
Figure 14: Stability of the Night - Middle (972568)

Figure 15: Stability of the Night - Late (972584)

16
7 Efficiency
Because there is no scientific laboratory capable of conducting research on con-
trol stars, experimental galaxies, or variable nebulae, astronomers substitute the
typical scientific experiment with an observation. These observations can then be
modeled and theories can be developed for future observations and models to be
compared against. One useful model for an observing system would be its effi-
ciency. Intuitively, many people are familiar with efficiency in terms of “how much
of is available is actually used.” in the case of the Hermes spectrograph this could
be restated as how many of the photons that enter the system are registered as
signal. A very general relationship for efficiency could be given as,
pdetected pdetected
= =
pdetected + pundetected ptotal
Necessarily the undetected and total photons will not be measured, but theoretical
values based on the magnitudes of stars from earth can be used as a benchmark to
compare our night’s worth of data to. For practical detection purposes, electrons
or current is used in place of photons; anyone familiar with CCDs, or signal pro-
cessing will quickly grasp the usefulness of switching what is measured to calculate
efficacy. In order to obtain theoretical values of The Exposure Time Calculator in
hermesETC.py was used; the output is also given in electrons rather than photons.
12 Objects were observed on my night, but only 11 of them had v-band magnitudes
listed on their VizieR website (see appendix for exception). The 11 observed ob-
jects were discovered from the headers of their first array of their corresponding fits
files. The thirtith and thirty first element in that array contained their exposure
times and object viewed respectively. V-band magnitudes of each star (except one)
could then be obtained from Vizier. Though three exposures were checked closely
and used for the purposes of this project all 11 objects, exposure times, v-band
magnitudes, and HermesETC.py runs were made and are available for viewing in
the appendix. The command which allowed me to obtain this information was,

$pythonHermesET C.py − f HRF − v 8.8 − a 2.0 − e 1800


where 8.8 is the magnitude in the v-band, and 1800 is the time in seconds of the
exposure. The program provided theoretical amount of electrons being generated
by the system from the light of the observed star. For each of the runs of the
program, there were a few things that were constant in the results: the wave-
lengths groups or divisions were 380, 440, 550, 640, 790, and 880nm in each; the
% efficiencies for the above wavelengths were 5.7, 11, 15.2, 11.6, 7.8, and 4.5%;
and which band of wavelength had the greatest signal to noise ratio and electrons
generated from the star. See the table of exposure outputs for details.

17
HD152614
Hermes HRF snr/flux
Vmag: 4.380 Exposure time: 330.00s
Binning: 1x1 Read noise:) 5.0e-
Spectral type: B E(B-V): 0.00
Air mass: 2.00 Moon: grey
Seeing: 1.20” Slitloss: 4.9%
Wavelength SNR e- star e- sky %Eff
380nm 308.2 95347.7 0.5 5.7
440nm 508.9 259322.1 0.2 11
550nm 513.7 264260.7 0.3 15.2
640nm 398.5 159195.1 0.3 11.6
790nm 290.7 84888.9 0.4 7.8
880nm 194.2 38074.8 0.4 4.5

Table 1: Exposure Time Calculator Results - HD152614

Other outputs, like the amount of the signal to noise ratios, and electrons gener-
ated from the star and sky changed with each run. Though the wavelength of 440
nm was always very close to the 550 nm signal in signal to noise ratio (consistently
some number just over 99% of it) and electrons generated by the star (consistently
some number just over 98% of it), the 550 nm wavelength was the maximally pro-
ductive as well as efficient band.

All of the theoretical derivations obtained by HermesETC are tabulated in the


appendix. One of them is available here.
Figures 16, 17 and 18 show the comparison that made between the observed values
of three stars and their theoretical values matched to the exposure time. Though
the margin of error is not small, the sloping trend and orders of magnitude can be
seen in reasonable agreement. Even though efficiency is a clearly defined scientific
variable and provides us with valuable information about the use and operation of
Hermes, we can most comfortably label the Hermes as an efficient system verbally
when we compare it to the other spectrographs that came before it and the ones
that are in use today.

8 Discussion
Over the course of the second 2/3rds of this semester, and especially in the month
of December, this project as led me to a wide understanding of spectroscopic data,
analyze the scientific instruments and their calibration used in their data acquisi-

18
Figure 16: Observed vs. Predicted E- Response - HD152614

Figure 17: Observed vs. Predicted E- Response - LS IV-1317

19
Figure 18: Observed vs. Predicted E- Response - WR153

tion, and create and evaluate a night of observations worth of data, a whole host
of new knowledge and familiarity was obtained. Though there have been assump-
tions and possibly unclarified assertions made in this work, vast knowledge on the
modern analysis of astronomical data and extensive growth in my capability to
understand and use python and Linux programs has occurred. If the goal of this
project was to bring one up to the level of familiarity where one can begin to im-
prove the existing Hermes or designing a superior one, then I must keep my nose to
the grindstone; but if the goal of this project has been to understand the lightpath
within spectroscopic instruments; use, extract information, and manipulate fits
files, learn about echelle spectrographs, and the relationship between their inter-
ference and blazing functions, as well as Littrow, quasi-Littrow, and non-Littrow
configurations which mathematically bind the blaze angles and wavelengths to the
geometry of the apparatus; spectral resolution, sampling, and free spectral range;
stability over the observing time of a night; spectroscopic efficiency; and how as-
tronomers can investigate each of these fields like a classical scientist by making
an observation, creating a model and working to improve one after another in the
iterative path of science, then I have certainly succeeded, it would not be an ex-
aggeration to say that I have chugged from the fountain of knowledge in my work
on this.

20
8.1 Error Analysis
No investigation into the constantly changing real world can be without uncer-
tainty and errors. This report has investigated many different types of errors, and
even considered the margins of error on errors themselves; for example when the
stability of a night, which is already a measure of error caused by atmospheric and
environmental forces, is determined to be approximately equal to a red-shift of 3
m/s, it also contained an error margin (namely of +/- 3 m/s). Other errors were
investigated more directly, like the error the spectrograph had in it’s wavelength
efficiencies on the night of observations. Theory should match calculations, and
discrepancies mean work is to be done in recalibrating or inventing superior in-
struments or in improving the model with more accurate constants, or additional
parameters to capture the (probably smaller) influence from other effects.

8.2 Future Works and Investigations


If given more time, and in particular more instruction on the use of python or
other software packages capable of advanced mathematical manipulation, I would
first and foremost find all of the peaks on all of the orders, allocate them into a
matrix or data frame, and then determine the FWHM on each by modeling each
peak with an error-minimized standard deviation of a normal distribution scaled
to the peak height of each signal. This means that I would do exactly what I did
for one peak for all peaks and tabulate those results clearly.

It would be a long-term project, but a deeper understanding of astronomical phe-


nomena as well as software proficiency would be developed in the undertaking of
analyzing the origins and implementation of Hermes python scripts. For example,
in calculating the efficiency of Hermes, a theoretical number of electrons (from
stellar photons) are generated based on the exposure time and magnitude of the
v band of the star and nothing else. Of course these are the primary or related
to the primary contributors to any observation, but any model that took these
things into account along with other relevant conditions of the observation, such
as magnitude in non-v bands, nightly temperature, humidity, time away from sun-
set or sunrise, would likely prove to be a more successful model. At least after
integrating it with the necessary software components that have been learned how
to use in this project, but are indeed still an area for future research.

9 Conclusion
This project has been overwhelmingly educational. I have had the great joy of
spending the vast majority of every waking minute of my winter holiday learning

21
about stars, spectra, and the ingenious contrivances that astronomers have de-
veloped to better understand the world we all live in. In particular, the breadth
and depth of familiarity that I now have with astrophysical instrumentation, ob-
servational methods, and computer programming languages like python and the
terminal is absolutely incomparable to that which I brought with me at the start
of the semester. Thank you for the opportunity to learn these invaluable concepts,
skills, and techniques.

10 References
1. HERMES Spectrograph. The Mercator Telescope. (2007) Instituut voor
Sterrenkunde K.U. Leuven http://hermes.ster.kuleuven.be/
https://web.archive.org/web/20070429083013/http://hermes.ster.kuleuven.be/

2. G. Raskin, H. Van Winckel, et. al. HERMES: a high-resolution fibre-


fed spectrograph for the Mercator telescope A&A 526, A69 (2011) DOI:
10.1051/0004-6361/201015435

3. Very Large Telescope, The world’s most advanced visible-light astronomical


observatory. ESO, the European Southern Observatory.
https://www.eso.org/public/teles-instr/paranal-observatory/vlt/

4. FITS, Wikipedia https://en.wikipedia.org/wiki/FITS

5. Raskin, Gert. HERMES, a fibre-fed high-resolution spectrograph for the


Mercator telescope, Dissertation presented in partial fulfilment of the re-
quirements for the degree of Doctor in Engineering. (2011). Arenberg Doc-
toral School of Science, Engineering & Technology Faculty of Engineering,
Department of Mechanical Engineering, KU Leuven

6. FITS File Handling (astropy.io.fits). astropy:docs.


https://docs.astropy.org/en/stable/io/fits/

7. scipy.signal.find peaks. SciPy.org.


https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.find peaks.html

8. Schroeder, Daniel J.. Astronomical Optics, Second Edition. San Diego,


Academic Press

22
11 Appendix A: Plots, Figures, and Graphs

Figure 19: Resolution Optimization Debugging Progress at Time of Submission

23
Number Wavelength color, top difference
1 3810.2 purple 41.0
2 3851.2 red 42.1
3 3893.3 green 43.0
4 3936.3 oj 43.8
5 3980.1 dk blue 45.4
6 4025.5 cyan 46.1
7 4071.6 olive 47.2
8 4118.8 gray 47.5
9 4166.3 pink 49.8
10 4216.1 brown 49.9
11 4266 purple 51.6
12 4317.6 red 54.0
13 4371.6 green 53.1
14 4424.7 oj 55.7
15 4480.4 dk blue 57.0
16 4537.4 cyan 57.7
17 4595.1 olive 60.2
18 4655.3 gray 61.0
19 4716.3 pink 64.0
20 4780.3 brown 64.9
21 4845.2 purple 66.4
22 4911.6 red 69.1
23 4980.7 green 70.4
24 5051.1 oj 72.8
25 5123.9 dk blue 74.3
26 5198.2 cyan 76.1
27 5274.3 olive 79.9
28 5354.2 gray 81.6
29 5435.8 pink 84.5
30 5520.3 brown 86.1
31 5606.4 purple 89.5
32 5695.9 red 92.1
33 5788 green 95.2
34 5883.2 oj 99.7
35 5982.9 dk blue 101.3
36 6084.2 cyan 105.9
37 6190.1 olive 109.8
38 6299.9 gray 114.1
39 6414 pink 116.6
40 6530.6 brown 121.1
41 6651.7 purple 126.7
42 6778.4 red 131.0
43 6909.4 green 136.3
44 7045.7 oj 141.5
45 7187.2 dk blue 147.9
46 7335.1 cyan 153.8
47 7488.9 olive 160.5
48 7649.4 gray 167.9
49 7817.3 pink 175.4
50 7992.7 brown 184.6
51 8177.3 24 purple 192.1
52 8369.4 red 197.9
53 8567.3 green 219.5
54 8786.8 oj
55 — dk blue

Table 2: Free Spectral Ranges


Signal peak’s
Pixel location in
order of orders
Order Pixel Order Order Order Order
1 1462 43 1488 86 3573
2 2082 44 2957 87 976
3 3625 45 4069 88 2693
4 3962 46 4434 89 1770
5 2847 47 615 90 2924
6 1933 48 1097 91 3265
7 2215 49 1723 92 670
8 4196 50 3171 93 1692
9 4376 51 1867 94 3517
10 1597 52 3343 95 1025
11 2877 53 4298 96 3053
12 2888 54 1057 97 3795
13 897 55 1411 98 1403
14 3937 56 1494 99 1867
15 4179 57 1700 100 3263
16 46 58 2355 101 784
17 1936 59 3202 102 2322
18 3326 60 3739 103 3033
19 3608 61 388 104 3483
20 2316 62 1181 105 1093
21 3030 63 2219 106 1422
22 1777 64 2568 107 3310
23 3814 65 1161 108 3474
24 3851 66 2143 109 1116
25 1019 67 3170 110 2251
26 1085 68 1972 111 3141
27 2262 69 3505 112 3497
28 2914 70 1887 113 1178
29 3626 71 3206 114 2218
30 3830 72 1654 115 2839
31 4274 73 2639 116 2937
32 1802 74 1721 117 2020
33 1464 75 2897 118 2999
34 2513 76 2657 119 3221
35 3562 77 3418 120 631
36 3588 78 2539 121 1533
37 2331 79 3354 122 1939
38 2547 80 2394 123 2048
39 2630 81 25 2685 124 1617
40 2761 82 1702 125 2848
41 4128 83 1788 126 1948
42 617 84 2026 127 3377
85 2387 128 1218
Observations 20200816
object mag v exposure t
HD 154345 — 30
HD152614 4.38 330
HD152614 4.38 330
HD164536 7.4 600
BD-114586 9.52 1800
LS IV -1317 11.474 3600
LS IV -1319 11 3000
WR155 8.8 450
WR123 11.12 1800
WR123 11.12 1800
HD 187567 6.478 600
KIC12365420 11.58 1800
HD 228256 10.02 1840
WR153 9 1800

HD152614
Hermes HRF snr/flux
Vmag: 4.380 Exposure time: 330.00s
Binning: 1x1 Read noise:) 5.0e-
Spectral type: B E(B-V): 0.00
Air mass: 2.00 Moon: grey
Seeing: 1.20” Slitloss: 4.9%
Wavelength SNR e- star e- sky %Eff
380nm 308.2 95347.7 0.5 5.7
440nm 508.9 259322.1 0.2 11
550nm 513.7 264260.7 0.3 15.2
640nm 398.5 159195.1 0.3 11.6
790nm 290.7 84888.9 0.4 7.8
880nm 194.2 38074.8 0.4 4.5

26
HD152614
Hermes HRF snr/flux
Vmag: 4.380 Exposure time: 330.00s
Binning: 1x1 Read noise:) 5.0e-
Spectral type: B E(B-V): 0.00
Air mass: 2.00 Moon: grey
Seeing: 1.20” Slitloss: 4.9%
Wavelength SNR e- star e- sky %Eff
380nm 308.2 95347.7 0.5 5.7
440nm 508.9 259322.1 0.2 11
550nm 513.7 264260.7 0.3 15.2
640nm 398.5 159195.1 0.3 11.6
790nm 290.7 84888.9 0.4 7.8
880nm 194.2 38074.8 0.4 4.5

BD-114586
Hermes HRF snr/flux
Vmag: 9.520 Exposure time: 1800.00s
Binning: 1x1 Read noise:) 5.0e-
Spectral type: B E(B-V): 0.00
Air mass: 2.00 Moon: grey
Seeing: 1.20” Slitloss: 4.9%
Wavelength SNR e- star e- sky %Eff
380nm 64.9 4571.5 2.8 5.7
440nm 109.8 12433.3 1.2 11
550nm 110.9 12670 1.4 15.2
640nm 85.2 7632.6 1.7 11.6
790nm 61 4070 2 7.8
880nm 38.8 1825.5 2.4 4.5

27
LS IV -1317
Hermes HRF snr/flux
Vmag: 11.474 Exposure time: 3600.00s
Binning: 1x1 Read noise:) 5.0e-
Spectral type: B E(B-V): 0.00
Air mass: 2.00 Moon: grey
Seeing: 1.20” Slitloss: 4.9%
Wavelength SNR e- star e- sky %Eff
380nm 34.6 1511.8 5.6 5.7
440nm 61.3 4111.6 2.4 11
550nm 61.9 4189.9 2.8 15.2
640nm 46.7 2524.1 3.4 11.6
790nm 32.3 1345.9 4.1 7.8
880nm 19.1 603.7 4.8 4.5

LS IV -1319
Hermes HRF snr/flux
Vmag: 11.000 Exposure time: 3000.00s
Binning: 1x1 Read noise:) 5.0e-
Spectral type: B E(B-V): 0.00
Air mass: 2.00 Moon: grey
Seeing: 1.20” Slitloss: 4.9%
Wavelength SNR e- star e- sky %Eff
380nm 40.3 1949.4 4.7 5.7
440nm 70.3 5301.9 2 11
550nm 71 5402.8 2.4 15.2
640nm 53.9 3254.8 2.9 11.6
790nm 37.6 1735.6 3.4 7.8
880nm 22.7 778.4 4 4.5

28
WR155
Hermes HRF snr/flux
Vmag: 8.800 Exposure time: 450.00s
Binning: 1x1 Read noise:) 5.0e-
Spectral type: B E(B-V): 0.00
Air mass: 2.00 Moon: grey
Seeing: 1.20” Slitloss: 4.9%
Wavelength SNR e- star e- sky %Eff
380nm 43.5 2218.2 0.7 5.7
440nm 75.4 6032.9 0.3 11
550nm 76.1 6147.8 0.4 15.2
640nm 58 3703.5 0.4 11.6
790nm 40.7 1974.9 0.5 7.8
880nm 24.9 885.8 0.6 4.5

WR123
Hermes HRF snr/flux
Vmag: 11.120 Exposure time: 1800.00s
Binning: 1x1 Read noise:) 5.0e-
Spectral type: B E(B-V): 0.00
Air mass: 2.00 Moon: grey
Seeing: 1.20” Slitloss: 4.9%
Wavelength SNR e- star e- sky %Eff
380nm 27.7 1047.3 2.8 5.7
440nm 50.1 2848.3 1.2 11
550nm 50.6 2902.5 1.4 15.2
640nm 37.9 1748.5 1.7 11.6
790nm 25.7 932.4 2 7.8
880nm 14.8 418.2 2.4 4.5

29
HD 187567
Hermes HRF snr/flux
Vmag: 6.478 Exposure time: 600.00s
Binning: 1x1 Read noise:) 5.0e-
Spectral type: B E(B-V): 0.00
Air mass: 2.00 Moon: grey
Seeing: 1.20” Slitloss: 4.9%
Wavelength SNR e- star e- sky %Eff
380nm 157.3 25103.9 0.9 5.7
440nm 260.6 68276.5 0.4 11
550nm 263.1 69576.8 0.5 15.2
640nm 203.8 41914.2 0.6 11.6
790nm 148.3 22350.3 0.7 7.8
880nm 98.3 10024.7 0.8 4.5

KIC12365420
Hermes HRF snr/flux
Vmag: 11.580 Exposure time: 1800.00s
Binning: 1x1 Read noise:) 5.0e-
Spectral type: B E(B-V): 0.00
Air mass: 2.00 Moon: grey
Seeing: 1.20” Slitloss: 4.9%
Wavelength SNR e- star e- sky %Eff
380nm 20.9 685.6 2.8 5.7
440nm 39.3 1864.6 1.2 11
550nm 39.8 1900.1 1.4 15.2
640nm 29.3 1144.6 1.7 11.6
790nm 19.3 610.4 2 7.8
880nm 10.7 273.8 2.4 4.5

30
HD 228256
Hermes HRF snr/flux
Vmag: 10.020 Exposure time: 1840.00s
Binning: 1x1 Read noise:) 5.0e-
Spectral type: B E(B-V): 0.00
Air mass: 2.00 Moon: grey
Seeing: 1.20” Slitloss: 4.9%
Wavelength SNR e- star e- sky %Eff
380nm 51.1 2948.5 2.9 5.7
440nm 87.5 8019.2 1.2 11
550nm 88.3 8171.9 1.4 15.2
640nm 67.6 4922.9 1.8 11.6
790nm 47.8 2625.1 2.1 7.8
880nm 29.8 1177.4 2.4 4.5

WR153
Hermes HRF snr/flux
Vmag: 9.000 Exposure time: 1800.00s
Binning: 1x1 Read noise:) 5.0e-
Spectral type: B E(B-V): 0.00
Air mass: 2.00 Moon: grey
Seeing: 1.20” Slitloss: 4.9%
Wavelength SNR e- star e- sky %Eff
380nm 83.7 7380 2.8 5.7
440nm 140.3 20071.8 1.2 11
550nm 141.7 20454 1.4 15.2
640nm 109.3 12321.9 1.7 11.6
790nm 78.8 6570.5 2 7.8
880nm 51.1 2947 2.4 4.5

31
Figure 20: Resolution (currently with one data point, ideally with 100s-1000s)

Figure 21: ThArNe Emissions for Spectroscopic Calibrations

32
12 Appendix B: Programming Codes

33
####################
# Import libraries #
####################
import numpy as np
from astropy.io import fits
from scipy.signal import find_peaks
from scipy.stats import norm
import matplotlib.pyplot as plt

# Make the plots professional and big


plt.style.use(’seaborn-poster’)

# Get data (flat field, calibration, and wavelength) from fits files
ff = fits.open(’00972537_HRF_FF_ext.fits’)[0].data
th_lamp = fits.open(’00972557_HRF_TH_ext.fits’)[0].data
wavelengths = np.transpose(fits.open(’00972557_HRF_TH_ext_wavelengthScale.fits’)[0].data)
#transpose because of the "histrical reason"

# Plot the data from the fits file


plt.plot(wavelengths, ff)
plt.title("Extracted flat-field in pixel-order space. Orders give individual blaze profiles of the orders")
plt.xlabel("Wavelengths [angstrom]")
plt.ylabel("Intensity in flat-field in pixel-order space")
plt.grid(linestyle=’dotted’)
plt.show()
# Plot #2
plt.plot(wavelengths, th_lamp)
plt.title("Extracted wavelength calibration frame. This is in pixel-order space")
plt.xlabel("Wavelengths [angstrom]")
plt.ylabel("Thorium-argon-neon lamp emissions") #plt.yscale(’log’) #plt.xscale(’log’)
plt.grid(linestyle=’dotted’)
plt.show()

########################
### Blaze wavelength ###
########################
# For each order between 40-94, find the wavelength with the highest flux value
# Test by hand/eye: ~#10’s WL should be ~5085.972 and ~#20 should be ~5477.282
ff_j = []
ff_max = []
ff_max_index = []
blaze_wavelength = []

# A new approach to getting the maximizing wavelengths


orders = list(range(len(ff[0,:])))
for i in orders: #1-55
order_counter = ff[:,i] #define an array that is all of the data within this i-th order
ff_max.append(np.amax(order_counter)) #find the max in this i-th order’s data (build an array of maxima)
ff_max_index.append(np.argmax(order_counter)) #find the index position of the maximum (build an array)
for i in orders:
# use ff_max_index[i], and not ff_max_index
blaze_wavelength.append(wavelengths[ff_max_index[i],i]) #find the wavelengths with the index positions

#print(’\tff_max (intensities), has a length (should be 55), and (should be 76105 to 395.5 and) then is: ’)
#print(len(ff_max))
#print(ff_max)
#print(’\tthese ff_max\’s (are of length and) occur at positions [__] in the [0,1,2...order number]: ’)
#print(len(ff_max_index))
#print(ff_max_index)
#print(’\tthe Blaze wavelengths (in angstrom) are: ’)
#print(’the (55 blaze) wavelengths, should go from 8882 to 3796 angstrom and, at these index positions are: ’)
#print(len(wavelength_max))
#print(blaze_wavelength)

34
###################
### Blaze Angle ###
###################
# Do calculations for blaze angles, blaze_angle = np.arcsin((m*wl)/(2*d))
BlazeAngle = []
grooves_per_mm = 52.676 #from lecture, slide 45 - characteristics. 1.9 *10^-5 m / groove
grooves_per_angstrom = grooves_per_mm*(10**3)*(10**-10) #1000 mm in 1 m, 10^10 A in 1 m
d = 1/grooves_per_angstrom #the distance between grooves (in A)
print(d)
for i in orders:
m = i + 40
BlazeAngle.append(np.arcsin(m*blaze_wavelength[i]/(2*d))) # m goes from 40 - 95
print(’the blaze angles [radians] are: ’)
#print(BlazeAngle)
BlazeAngleDegree = np.multiply(BlazeAngle,(180/np.pi))
#print(BlazeAngleDegree)

# Error Check #
# Check errors: analysis of differences between measured and
#theoretical/literature (Raskin paper) values of blaze wavelength
theoretical_BWL = [8905,8688,8481,8283,8095,7915,7743,
7578,7421,7269,7124,6984,6850,6721,6596,6476,6361,6249,
6141,6037,5936,5839,5745,5654,5565,5480,5397,5316,5238,
5162,5088,5017,4947,4879,4813,4749,4687,4626,4567,4509,
4452,4397,4344,4291,4240,4190,4142,4094,4048,4002,3958,3914,3872,3830,3789]
difference = np.subtract(theoretical_BWL, blaze_wavelength) #this will give differences,
but will be washed out by the positive and negative variations
diff_squared = np.square(difference) #now the errors are all positive
sum_diff_sqrd = np.sum(diff_squared) #this gives a number for disscussions of accuracy
print(’The sum of the squared errors is: ’)
print(sum_diff_sqrd)
# Plot the difference between the theoretical & calculated over each order
plt.clf() #clears the current plot
plt.scatter(np.add(orders,40), blaze_wavelength, label=’calculated wavelengths’)
plt.scatter(np.add(orders,40), theoretical_BWL, label=’theoretical wavelengths’, marker=’x’, s=25)
plt.title("Blaze-wavelengths: Calculations & literature")
plt.xlabel("Order number (a proxy for wavelength [A] band)")
plt.ylabel("Wavelengths [Angstrom]")
plt.grid(linestyle=’dotted’)
plt.legend()
plt.show()
plt.clf()
plt.scatter(np.add(orders,40), diff_squared)
plt.title("Blaze-wavelengths magnitudes of errors")
plt.xlabel("Order number (a proxy for wavelength [A] band)")
plt.ylabel("Difference values squared [Angstrom^2]")
plt.grid(linestyle=’dotted’)
plt.show()
plt.clf()
plt.scatter(np.add(orders,40), difference)
plt.title("Blaze-wavelength errors: Differences between calculations & literature")
plt.xlabel("Order number (a proxy for wavelength [A] band)")
plt.ylabel("Difference values [Angstrom]")
plt.grid(linestyle=’dotted’)
plt.show()

#############################
### Resolution (sampling) ###
#############################
# R is the wavelength over the change in wavelength better explination in report
# R = lamda/delta_lamda
# lamda is the wavelength at an emission/absorption line
# delta_lamda is approximately the FWHM of the signal peak
# Sampling per resoltion element is the number of pixels in the width of a signal (aka a resolution element)
# "how many pixels do you need to capture the FWHM? That’s delta_lamda and the sampling"
35
# Get data
th_fits1 = fits.open(’00972557_HRF_TH_ext.fits’)[0].data #use the raw-ish HighRes TH files
# Plot the data from the fits file
plt.clf() #clears the current plot
plt.plot(wavelengths, th_fits1)#, label=’regular’)
#plt.plot(wavelengths, obj_no_cosm_fits, label=’object, no cosmic’)
plt.title("First calibration plot of 00972557_HRF_TH_ext")
plt.xlabel("wavelengths [angstrom]")
plt.ylabel("ThArNe emission lines")
plt.grid(linestyle=’dotted’) #plt.yscale(’log’) #plt.xscale(’log’)
plt.legend()
plt.show() # This graph is a forest of intensity lines over 55 orders
# 1 signal peak (plot them with an x), there’s ~1000 in total
th_fitsPreForLoop = th_fits1[:,4]
wavelengthPreForLoop = wavelengths[:,4] # the purple order with 4 peaks
print(’\tthe wavelengthPreForLoop is: ’) # should find peaks at 8115, 8104, 8014, & 8006 A
print(wavelengthPreForLoop) # or with pixel numbers between 1 - 4806
pixelPreForLoop = []
for i in range(len(wavelengthPreForLoop)):
pixelPreForLoop.append(i) #shift the wavelengths from their angstrom band down to their pixel number
print(’\tthe pixels of this order are: ’)
print(pixelPreForLoop)
print(’\tthe intensities of th_fits[:,4] are: ’) # with intensities 978000, 936000, 877000, 826000
print(th_fits1[:,4])
signal_peak_pixels = find_peaks(th_fits1[:,4], height=790000)[0] #there is no [2]!
signal_peak_heights_complex = find_peaks(th_fits1[:,4], height=790000)[1]
#don’t need this just use "th_fits1[signal_peak_pixels,4]"
for i in range(len(signal_peak_pixels)): #there will be ~1000 in total
signal_peak_heights = th_fitsPreForLoop[signal_peak_pixels]
print(’\tthe signal_peak_pixels (signals have peaks) located at the pixels: ’)
print(signal_peak_pixels)
print(’\tthe signal_peak_heights (signals have peak heights of) : ’)
print(signal_peak_heights)
plt.clf() #clears out the old plot
plt.plot(pixelPreForLoop, th_fitsPreForLoop)
plt.scatter(signal_peak_pixels, signal_peak_heights, marker="x", s=200, c=’tab:orange’)
plt.title("Finding signal maxima in 5th order of 00972557_HRF_TH_ext")
plt.xlabel("Pixel number within 5th order (proportional to wavelength)")
plt.ylabel("ThArNe emission lines")
plt.grid(linestyle=’dotted’)
plt.show() #saved 29 Dec: shows 1 order Th intensities and its 4 peaks
#plt.plot(wavelengthPreForLoop, wavelengthPreForLoop[signal_peak_pixels], "wavelength") #plt.plot(wavelengths)
#plt.plot(np.zeros_like(wavelengths), "--", color="gray") #from the signal_peaks documentation

# Fit 1 signal with a gaussian distribution, eyeball estimate the sigma


normal_domain = []
mean = signal_peak_pixels[0]
sigma = 1.65 #pixel? optimize this
domain = 40
increment = 0.1
for i in list(range(int(domain/increment))):
# this defines the domain of the normal function. i.e., mean = 100, domain = (80,120)
normal_domain.append((signal_peak_pixels[0]-(domain/2)) + (i*increment))
normal_distribution = (np.exp(-((np.divide(np.subtract(normal_domain,mean),sigma))**2)/2))/
(np.multiply(sigma,np.sqrt(2*np.pi)))
print(signal_peak_heights[0])
scaled_norm_dist = (np.sqrt(2*np.pi))*sigma*signal_peak_heights[0]*normal_distribution
#increase the max from 1 to the max
# Now optimize the sigma... or take a different approach:
#interpolate the points that have 50\% the peak’s intensity and calculate their distance apart
#distribution = np.random.normal(loc=signal_peak_pixels[0], scale=1, size=signal_peak_heights[0])
#Optimize the "scale=1"
mean,std = norm.fit(normal_distribution)
fwhm = signal_peak_heights[0]*2*sigma*np.sqrt(2*np.log(2))
print(’the FWHM and resolution is: ’)
print(fwhm) 36
plt.clf() #clears out the old plot
plt.plot(pixelPreForLoop, th_fitsPreForLoop, marker=’o’, label=’Calibration data’)
plt.scatter(signal_peak_pixels[0], signal_peak_heights[0], marker="x", s=200, c=’tab:red’)
plt.plot(normal_domain,scaled_norm_dist, label=’Gaussian model’)
plt.xlim(signal_peak_pixels[0]-(domain/5), signal_peak_pixels[0]+(domain/5))
plt.title("A normal distribution modeling a Th emission for resolution and sampling")
plt.xlabel("Pixel number (proportional to wavelength)")
plt.ylabel("ThArNe emission signal")
plt.grid(linestyle=’dotted’)
plt.legend()
plt.show()
# Fit 1 signal with a gaussian distribution, minimize sum squared errors of sigma
normal_domain = []
sigma = []
min_differences = []
min_sigma = []
diff_norm_data = []
integ_model = []
mean = signal_peak_pixels[0]
domain = 40
increment = 0.1
for i in list(range(50)): # maybe make this 500 and 0.01 & 100 below for accuarecy
sigma.append(0.1+(i/10)) #so many pixels wide for a gaussian model (50/10 makes sigma = 5 )
print(’sigma is: ’ + str(sigma) +’\n\n’)
for i in list(range(len(sigma))): #using all the sigmas (widenesses of a normal distribution)
for j in list(range(int(domain/increment))):
# this defines the domain of the normal function. i.e., mean = 100, domain = (80,120)
normal_domain.append((signal_peak_pixels[0]-(domain/2)) + (j*increment))
normal_distribution = (np.exp(-((np.divide(np.subtract(normal_domain,mean),sigma[i]))**2)/2))/
(np.multiply(sigma[i],np.sqrt(2*np.pi)))
#print(signal_peak_heights[0])
scaled_norm_dist = (np.sqrt(2*np.pi))*sigma[i]*signal_peak_heights[0]*normal_distribution
#increase the max from 1 to the max
#set up the x-axis to be the peak +/- 8 pixels
spl_cent = int(np.where(th_fitsPreForLoop==signal_peak_heights[0])[0])
#spl = signal peak locale
spl = [spl_cent-8, spl_cent-7, spl_cent-6, spl_cent-5, spl_cent-4,
spl_cent-3, spl_cent-2, spl_cent-1, spl_cent, spl_cent+1,
spl_cent+2, spl_cent+3, spl_cent+4, spl_cent+5, spl_cent+6,
spl_cent+7, spl_cent+8]
#[1925, 1926, 1927, 1928, 1929, 1930, 1931, 1932, 1933,
1934, 1935, 1936, 1937, 1938, 1939, 1940, 1941]
splh = th_fitsPreForLoop[spl]
integ_data = np.trapz(splh, x=spl) # the integral of the data. For this peak it should be: 4146246.78502263
# cut the model’s integral so that it’s only over the local area of the peak
normal_domain_truc =[]
for j in list(range(len(normal_domain))):
#print(’j is: ’)
#print(j)
#print(’normal_domain[j] is: ’)
#print(normal_domain[j])
spl_min = spl_cent-8
spl_max = spl_cent+8
if normal_domain[j] >= spl_min and normal_domain[j] <= spl_max:
#print(’enter the if statement’)
normal_domain_truc.append(normal_domain[j])
#scaled_norm_dist_truc.append(scaled_norm_dist[normal_domain[j]])
integ_model.append(np.trapz(scaled_norm_dist))#, dx=0.1) # the integral of the model[i]
print(’integ_model:’)
print(integ_model)
#print(’sigma is: ’ + str(sigma[i]))
diff_norm_data.append(np.square(np.subtract([integ_data],[integ_model]))[0][0])
#the differences squared between the data and the model[i]s’ integrals
print(’diff_norm_data:’)
print(diff_norm_data)
min_differences.append(np.amin(diff_norm_data)) 37
#finds the minimum of
best_sigma = (np.argmin(diff_norm_data))
#distribution = np.random.normal(loc=signal_peak_pixels[0], scale=1, size=signal_peak_heights[0])
#Optimize the "scale=1"
#mean,std = norm.fit(normal_distribution)
print(’\tinteg_model is: ’)
print(integ_model) #print(integ_data)
print(’\tthe diff_norm_data, difference between the normal dist. and model squared is: ’)
print(diff_norm_data)
print(’\tthe min_differences, smallest difference is: ’)
print(min_differences)
print(’the best sigma (that causes the minimal difference) is: ’)
print(best_sigma)
scaled_norm_dist_opti = (np.sqrt(2*np.pi))*best_sigma*signal_peak_heights[0]*normal_distribution
print(’scaled_norm_dist_opti: ’)
print(scaled_norm_dist_opti)
plt.clf()
plt.plot(pixelPreForLoop, th_fitsPreForLoop, marker=’o’, label=’Calibration data’)
plt.plot(normal_domain,scaled_norm_dist_opti, label=’Optimized Gaussian model’)
plt.xlim(signal_peak_pixels[0]-(domain/5), signal_peak_pixels[0]+(domain/5))
plt.title("A normal distribution modeling a Th emission for resolution and sampling")
plt.xlabel("Pixel number (proportional to wavelength)")
plt.ylabel("ThArNe emission signal")
plt.grid(linestyle=’dotted’)
plt.legend()
plt.show()
# Now turn the above section of code into a for loop over all of the signal_peak_pixels[0, 1, 2, ... n]
# Now turn the above section of code into a for loop over all of the th_fits1[:,0],[:,1], ... [:,n]

# Find the FWHM of each peak


best_sigma = 1.67 #From the program before doing a least-squared-error approach to sigma
fwhm = 2*best_sigma*np.sqrt(2*np.log(2)) # or 2.355*sigma this will become fwhm.append()
#fwhm = 2*sigma*np.sqrt(2*np.log(2)) # or 2.355*sigma this will become fwhm.append()
print(’the FWHM is: ’)
print(fwhm)

# Recognize each signal’s delta_lamda = the FWHM of its gaussian distribution


# Calculate the Rs = lamda/delta_lamda = pixel_location/fwhm
resolution = signal_peak_pixels[0]/fwhm # this will become resolution.append()
print(’the resolution in this order at this pixel is: ’)
#print(resolution)

# Plot the R’s as a function of lamda (or pixel number)


plt.scatter(signal_peak_pixels[0], resolution, c=’g’, label=’order #5’) #DOUBLE CHECK DATA!
plt.xlim(0,4068)
plt.title("The single accurate resolution obtained; the rest after for-loop debugging")
plt.xlabel("Pixel number (proportional to wavelength)")
plt.ylabel("Resolution, R")
plt.legend()
plt.show()

# Get all of the peaks in all of the orders that are over 200000 in intensity
signal_peak_pixels_all = [] #initialize
for i in list(range(len(th_fits1[0,:]))): #all orders that have a 0th pixel, should be 4608
signal_peak_pixels_i = np.array(find_peaks(th_fits1[:,i], height=200000)[0]) #all data in i-th order
for j in list(range(len(signal_peak_pixels_i))):
signal_peak_pixels_all.append(signal_peak_pixels_i[j])
print(’signal_peak_pixels_all: ’)
print(signal_peak_pixels_all)
# make an array that has all of the peak heights from the array of where their index values are
#for i in list(range(len(signal_peak_pixels_all[0,:]))):
# for j in list(range(len(signal_peak_pixels_all[:,i]))):
# signal_peak_hights_all = th_fits1[signal_peak_pixels_all]
#for j in list(range(len(th_fits1[:, i]))): # within the i-th order

38
############################################
### Stability "(velocity) of the night" ####
############################################
# Find out how much radial velocity changes/"shifts" (due to weather, air-mass quantity changes
# Watch the ThArNe emissions move throughout the night (beginning, middle, and end)
# Use ThArNe and wavescale files in reduced folder with HermesVR
# Cookbook: python hermesVR.py -i nnnnnn [-w nnnnnn] [-v ff.f] [-b ff.f] [-e] [-t]
[-L or -LL] [-p] [-c][-m {n pathToMask}] [-f] [-ROT] [-o nn]
print(’the radial velocity was found to, on average, be 0.003 +/- 0.003 km/s’)
print(’3 +/- 0.3 m/s in the beginning of the night’)
print(’2 +/- 0.3 m/s in the middle of the night’)
print(’4 +/- 0.3 m/s in the end of the night’)

###########################
### Free Spectral Range ###
###########################
#Orders overlap and photons can go into either.
# Find the range that orders don’t overlap, and any photon
traveling into that order is guarenteed to belong there (4128.1)
intersections = [3810.2,3851.2,3893.3,3936.3,3980.1,4025.5,4071.6,
4118.8,4166.3,4216.1,4266.0,4317.6,4371.6,4424.7,4480.4,4537.4,
4595.1,4655.3,4716.3,4780.3,4845.2,4911.6,4980.7,5051.1,5123.9,
5198.2,5274.3,5354.2,5435.8,5520.3,5606.4,5695.9,5788,5883.2,
5982.9,6084.2,6190.1,6299.9,6414,6530.6,6651.7,6778.4,6909.4,
7045.7,7187.2,7335.1,7488.9,7649.4,7817.3,7992.7,8177.3,8369.4,
8567.3,8786.8]
maximum = 300000
plt.clf()
plt.plot(wavelengths, ff)
for i in list(range(len(intersections))):
plt.plot([intersections[i],intersections[i]],[0,maximum],c=’k’,linewidth=0.8)
plt.title("Free Spectral Ranges Between Orders")
plt.xlabel("wavelengths [angstrom]")
plt.ylabel("Intensity")
plt.xlim(3600,9200)
plt.grid(linestyle=’dotted’)
plt.show()
intersec_differences = []
for i in list(range(len(intersections)-1)):
intersec_differences.append(intersections[i+1] - intersections[i])
xaxis_differences = np.add(list(range(len(intersec_differences))), 40)
plt.scatter(xaxis_differences, intersec_differences)
plt.title("Size of each Free Spectral Range over Orders")
plt.xlabel("Order Number")
plt.ylabel("Size of Free-Spectral Range [angstrom]")
plt.grid(linestyle=’dotted’)
plt.show()

##################
### EFFICIENCY ###
##################
# E is the number of photons (use electrons for ease of measurement) used/dectected/that work
divided by the total number of photons that enter the spectrograph
# Get the number of photons detected from the data

# Get the number of photons detected theoretically


#open fits file for objects, see info/header for the information needed to get theoretical parameters
# # # # # # # # # # object mag_v exptime
objfits1 = fits.open(’00972571_HRF_OBJ_ext.fits’)[0].header # HD 154345 --- 30
objfits2 = fits.open(’00972572_HRF_OBJ_ext.fits’)[0].header # HD152614 4.38 330
objfits3 = fits.open(’00972573_HRF_OBJ_ext.fits’)[0].header # HD152614 4.38 330
objfits4 = fits.open(’00972574_HRF_OBJ_ext.fits’)[0].header # HD164536 7.40 600
objfits5 = fits.open(’00972575_HRF_OBJ_ext.fits’)[0].header # BD-114586 9.52 1800
objfits6 = fits.open(’00972576_HRF_OBJ_ext.fits’)[0].header # LS IV -1317 11.474 3600
objfits7 = fits.open(’00972577_HRF_OBJ_ext.fits’)[0].header # LS IV -1319 11.00 3000
39
objfits8 = fits.open(’00972578_HRF_OBJ_ext.fits’)[0].header # WR155 8.80 450
objfits9 = fits.open(’00972579_HRF_OBJ_ext.fits’)[0].header # WR123 11.12 1800
objfits10 = fits.open(’00972580_HRF_OBJ_ext.fits’)[0].header # WR123 11.12 1800
objfits11 = fits.open(’00972581_HRF_OBJ_ext.fits’)[0].header # HD 187567 6.478 600
objfits12 = fits.open(’00972582_HRF_OBJ_ext.fits’)[0].header # KIC12365420 11.58 1800
objfits13 = fits.open(’00972583_HRF_OBJ_ext.fits’)[0].header # HD 228256 10.02 1840
objfits14 = fits.open(’00972587_HRF_OBJ_ext.fits’)[0].header # WR153 9.00 1800
#find the "OBJECT = ___________" section, then go to ViZieR to get magnitude in v band
print(’show the header: ’)
print(list(objfits1))
print(’get the number of orders in observation #1 (should be 55): ’)
print(objfits1[3])
print(’get the location of observatory for object #1 (should be LaPalma): ’)
print(objfits1[7])
all_objects = [objfits1, objfits2, objfits3, objfits4, objfits5, objfits6,
objfits7, objfits8, objfits9, objfits10, objfits11, objfits12, objfits13, objfits14]
# Plot the objects, get their e- at the wavelengths in the theoretical efficiencies
wavelengths = np.transpose(fits.open(’00972557_HRF_TH_ext_wavelengthScale.fits’)[0].data)
#transpose because of the "histrical reason"
objfits2data = fits.open(’00972572_HRF_OBJ_ext.fits’)[0].data # HD152614 4.38 330
plt.clf()
plt.plot(wavelengths, objfits2data)
plt.title("HD152614 Object spectra mag_v = 4.38 ExpTime = 330 for efficiency comparison")
plt.xlabel("wavelengths [angstrom]")
plt.ylabel("intensity")
plt.grid(linestyle=’dotted’)
plt.show() #intensity at 380nm, 440, 550, 640, 790, 880 are: 2850, 194000, 219000, 140000, 126000, 48000
objfits6data = fits.open(’00972576_HRF_OBJ_ext.fits’)[0].data # LS IV -1317 11.474 3600
plt.plot(wavelengths, objfits6data)
plt.title("LS IV -1317 Object spectra mag_v = 11.474 ExpTime = 3600 for efficiency comparison")
plt.xlabel("wavelengths [angstrom]")
plt.ylabel("intensity")
plt.grid(linestyle=’dotted’)
plt.show() #intensity at 380nm, 440, 550, 640, 790, 880 are: 200, 2800, 16000, 18000, 32000, 16000
objfits14data = fits.open(’00972587_HRF_OBJ_ext.fits’)[0].data # WR153 9.00 1800
plt.plot(wavelengths, objfits2data)
plt.title("WR153 Object spectra mag_v = 9.00 ExpTime = 1800 for efficiency comparison")
plt.xlabel("wavelengths [angstrom]")
plt.ylabel("intensity")
plt.grid(linestyle=’dotted’)
plt.show() #intensity at 380nm, 440, 550, 640, 790, 880 are: 2800, 194000, 219000, 140000, 126000, 47000
# Now plot the Theory Vs. Measured in each of the 3 objects
wavelengths_efficiency = [3800,4400,5500,6400,7900,8800]
theoryelectron2 = [95347.7,259322.1,264260.7,159195.1,84888.9,38074.8]
measuredelectron2 = [2850,194000,219000,140000,126000,48000]
theoryelectron6 = [1511.8,4111.6,4189.9,2524.1,1345.9,603.7]
measuredelectron6 = [200,2800,16000,18000,32000,16000]
theoryelectron14 = [7380,20071.8,20454,12321.9,6570.5,2947]
measuredelectron14 = [2800,194000,219000,140000,126000,47000]
plt.clf()
plt.plot(wavelengths_efficiency, theoryelectron2, ’-o’,linewidth=0.6, label=’theory’)
plt.plot(wavelengths_efficiency, measuredelectron2, ’-o’,linewidth=0.6, label=’measured’)
plt.title("Efficiency: HD152614 Object spectra measured vs. theory comparison")
plt.xlabel("wavelengths [angstrom]")
plt.ylabel("intensity")
plt.xlim(3000,9500)
plt.ylim(-50000,350000)
plt.grid(linestyle=’dotted’)
plt.legend()
plt.show()
plt.plot(wavelengths_efficiency, theoryelectron6, ’-o’,linewidth=0.6, label=’theory’)
plt.plot(wavelengths_efficiency, measuredelectron6, ’-o’,linewidth=0.6, label=’measured’)
plt.title("Efficiency: LS IV -1317 Object spectra measured vs. theory comparison")
plt.xlabel("wavelengths [angstrom]")
plt.ylabel("intensity")
plt.xlim(3000,9500)
plt.ylim(-10000,50000)
plt.grid(linestyle=’dotted’)
plt.legend()
plt.show()

40
plt.plot(wavelengths_efficiency, theoryelectron14, ’-o’,linewidth=0.6, label=’theory’)
plt.plot(wavelengths_efficiency, measuredelectron14, ’-o’,linewidth=0.6, label=’measured’)
plt.title("Efficiency: WR153 Object spectra measured vs. theory comparison")
plt.xlabel("wavelengths [angstrom]")
plt.ylabel("intensity")
plt.xlim(3000,9500)
plt.ylim(-60000,300000)
plt.grid(linestyle=’dotted’)
plt.legend()
plt.show()

j = 1
for i in all_objects: #show each header
#print(’\tthe #’+str(j)+’ object is’)
#print(i[31])
#print(’the exposure time on it was: ’)
#print(i[30])
j = j + 1
all_objects = [objfits2, objfits3, objfits4, objfits5, objfits6, objfits7, objfits8, objfits9, objfits10,
objfits11, objfits12, objfits13, objfits14] #this removes the First-MagnitudeInVBandLess-Unused object
# Get the total estimated/theoretical number of photons entered
from the HermesETC.py file (w/ star mag & exposure time)
#find the e-*...\$python HermesETC.py -f HRF -v ____ -a 2.0 -e ____
# Find the theoretical/total number of e-* that should come into the detector

# Efficiency = photons_detected * 100 / photons_total


# print(Efficiency)

’’’ ###########################################################
### Testing Code, copy and paste for use (and old bad code) ###
print(’ff is: ’)
print(ff)
print(’th_lamp is: ’)
print(th_lamp)
print(’wavelengths is: ’)
print(wavelengths)

print(scaled_norm_dist)
print(normal_domain_truc)

print(’the domain (should be around 1933 (1913-1953) and) is: ’)


print(normal_domain)

#truncatevaluepos = signal_peak_pixels[0] + 8
#truncatevalueneg = signal_peak_pixels[0] - 8
#normal_domain_truncated = normal_domain[truncatevalueneg < signal_peak_pixels[0] < truncatevaluepos]
#print(’model_domain_truncated is :’)
#print(normal_domain_truncated)

order1 = ff[:, 0] #which has a length of 4608


ff1_max = np.amax(order1)
ff1_max_index = np.argmax(order1)
wavelength1_max = wavelengths[ff1_max_index, 0]
print(’ff1_max, should be 76106, and is: ’)
print(ff1_max)
print(’this ff1_max occurs at (whatever) index number/position [__,0]: ’)
print(ff1_max_index)
print(’the (blaze) wavelength, should be 8882 and, at this index position is: ’)
print(wavelength1_max)
print(’\ n \ n \ n’)
# Now repeat for the other 54 orders (define a function and/or run a for loop)

41
#print(list(range(len(ff[1]))))
#print(list(range(len(ff[1,:]))))

# find how to isolate the first element and first element’s first element in a matrix
print(’this will be the first elements first element (its length = 1) ~27525’)
print(ff[0,0])
#print(len(ff[0,0]))
print(’this will be the first element/array (and length) ~[27525 26693 ... 0]’)
print(ff[0,:])
print(len(ff[0,:])) # this is 55
print(’this will be the first element inside of each array/element (and length of 4608) ~[27525 29731 ... 35408]’)
print(ff[:,0])
print(len(ff[:,0])) # this is 4608

#if i % 31 == 0:

### Excessively complex, throw out below ###


old_ff_j = []
old_ff_max = []
for i in list(range(len(ff[0,:]))): #order_ranges, there are 55 (from 40-95), this is [0 1 2 ... 54]
for j in list(range(len(ff[:,0]))): #order_data_points, there are 4608, this is [0 1 2 ... 4607]
#print(j)
#print(ff[j,0])
old_ff_j.append(ff[j,0]) #to the end of ff_j, add the j-th’s array’s first element
#print(’ff_j is: ’)
#print(ff_j)
print(’ff_j should be 4608 long, and has a length of: ’)
print(len(ff_j))
old_ff_max.append(np.amax(old_ff_j))
print(’there, should be 55, and are a total maximum intensity of: ’)
print(len(old_ff_max))
print(’all of the (55?) maximizing intensities are: ’)
print(old_ff_max)
# Now find the wavelengths that correspond to the maximum intensities
### Excessively complex, throw out above ###
’’’

42

You might also like