Numerical Python in Astronomy and Astrophysics: A Practical Guide to Astrophysical Problem Solving (Undergraduate Lecture Notes in Physics) Wolfram Schmidt - Download the complete ebook in PDF format and read freely
Numerical Python in Astronomy and Astrophysics: A Practical Guide to Astrophysical Problem Solving (Undergraduate Lecture Notes in Physics) Wolfram Schmidt - Download the complete ebook in PDF format and read freely
com
OR CLICK HERE
DOWLOAD EBOOK
Wolfram Schmidt
Marcel Völschow
Numerical
Python in
Astronomy and
Astrophysics
A Practical Guide to Astrophysical
Problem Solving
Undergraduate Lecture Notes in Physics
Series Editors
Neil Ashby, University of Colorado, Boulder, CO, USA
William Brantley, Department of Physics, Furman University, Greenville, SC, USA
Matthew Deady, Physics Program, Bard College, Annandale-on-Hudson, NY, USA
Michael Fowler, Department of Physics, University of Virginia, Charlottesville,
VA, USA
Morten Hjorth-Jensen, Department of Physics, University of Oslo, Oslo, Norway
Michael Inglis, Department of Physical Sciences, SUNY Suffolk County
Community College, Selden, NY, USA
Barry Luokkala , Department of Physics, Carnegie Mellon University, Pittsburgh,
PA, USA
Undergraduate Lecture Notes in Physics (ULNP) publishes authoritative texts
covering topics throughout pure and applied physics. Each title in the series is
suitable as a basis for undergraduate instruction, typically containing practice
problems, worked examples, chapter summaries, and suggestions for further reading.
ULNP titles must provide at least one of the following:
• An exceptionally clear and concise treatment of a standard undergraduate
subject.
• A solid undergraduate-level introduction to a graduate, advanced, or
non-standard subject.
• A novel perspective or an unusual approach to teaching a subject.
ULNP especially encourages new, original, and idiosyncratic approaches to physics
teaching at the undergraduate level.
The purpose of ULNP is to provide intriguing, absorbing books that will continue
to be the reader’s preferred reference throughout their academic career.
Numerical Python
in Astronomy
and Astrophysics
A Practical Guide to Astrophysical Problem
Solving
123
Wolfram Schmidt Marcel Völschow
Hamburg Observatory Hamburg University of Applied Sciences
University of Hamburg Hamburg, Germany
Hamburg, Germany
This Springer imprint is published by the registered company Springer Nature Switzerland AG
The registered company address is: Gewerbestrasse 11, 6330 Cham, Switzerland
Preface
Over the last decades the work of astronomers and astrophysicists has undergone
great changes. Making observations is an essential part of astronomy, but most
researchers do not operate instruments directly any longer. Most of the time they
receive huge amounts of data from remote or even space-bound telescopes and
make heavy use of computing power to filter, process, and analyse these data. This
requires sophisticated algorithms and, these days, increasingly utilizes machine
learning. On the theoretical side of astrophysics, making important discoveries just
with pencil and paper belongs to the past (with the occasional exception from the
rule). Scientific questions in contemporary astrophysics are often too complex to
allow for analytic solutions. As a consequence, numerical computations with a great
wealth of physical details play a major role in research now. Back-of-the-envelope
calculations still serve their purpose to guide researchers, but at the end of the day it
is hardly possible to make progress without writing and running code on computers
to gain a deeper understanding of the physical processes behind observed
phenomena.
In this regard, it is surprising that the education of students at the undergraduate
level is still largely focused on traditional ways of problem solving. It is often
argued that being able to program comes along the way, for example, when students
engage with their research project for a Bachelor’s thesis. It is said that problems in
introductory courses can be solved mostly with analytic techniques, and there is no
need to bother students with programming languages. However, we are convinced
that there is a great deal of computer-based problem solving that can be done right
from the beginning. As a result, connections to contemporary science can be made
earlier and more lively. One of the obvious merits of becoming acquainted with a
programming language is that you can learn how to address a question by devel-
oping and implementing an algorithm that provides the answer.
There are two major avenues toward learning a programming language. One
follows the systematic teaching model, where the language is laid out in all details
and you are guided step by step through its elements and concepts. Surely, this is
the preferable method if you want to master a programming language. For the
beginner, however, this can become tiresome and confusing, especially since the
v
vi Preface
relevance of most of the stuff you learn becomes clear only later (if at all). The
alternative approach is to learn mainly from examples, to grasp the language in an
intuitive way and to gradually pick up what you need to know for practical
applications. We believe that Python is quite suitable for this approach. Of course,
there is always a downside. This textbook is far from covering everything there is to
know about Python. We focus on numerical computation and data analysis and
make use of libraries geared toward these applications.
Problem solving is an art that requires a lot of practice. The worked-out
examples in this book revolve around basic concepts and problems encountered in
undergraduate courses introducing astronomy and astrophysics. The complete
source code is provided on the web via uhh.de/phy-hs-pybook. We briefly reca-
pitulate essential formulas and basic knowledge, but our recaps are by no means
intended to replace lecture courses and textbooks on astronomy and astrophysics.
This is highlighted by frequently referring to introductory textbooks for further
reading. Our book is mainly intended for readers who want to learn Python from
scratch. In the beginning, code examples are explained in detail, and exercises start
at a rather elementary level. As topics become more advanced, you are invited to
work on problems that require a certain amount of effort, time, and innovative
thinking. If you have already experience with programming and know some
Python, you can concentrate on topics you are interested in. Our objective is that
examples as well as exercises not only help you in understanding and using Python
but also offer intriguing applications in astronomy and astrophysics.
vii
Contents
1 Python Basics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.1 Using Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2 Understanding Expressions and Assignments . . . . . . . . . . . . . . . . 3
1.3 Control Structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.4 Working with Modules and Objects . . . . . . . . . . . . . . . . . . . . . . . 14
2 Computing and Displaying Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.1 Spherical Astronomy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.1.1 Declination of the Sun . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
2.1.2 Diurnal Arc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
2.1.3 Observation of Celestial Objects . . . . . . . . . . . . . . . . . . . . 35
2.2 Kepler’s Laws of Planetary Motion . . . . . . . . . . . . . . . . . . . . . . . 42
2.3 Tidal Forces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
3 Functions and Numerical Methods . . . . . . . . . . . . . . . . . . . . . . . . . . 55
3.1 Blackbody Radiation and Stellar Properties . . . . . . . . . . . . . . . . . 55
3.1.1 Stefan–Boltzmann Law . . . . . . . . . . . . . . . . . . . . . . . . . . 56
3.1.2 Planck Spectrum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
3.2 Physics of Stellar Atmospheres . . . . . . . . . . . . . . . . . . . . . . . . . . 77
3.2.1 Thermal Excitation and Ionization . . . . . . . . . . . . . . . . . . 78
3.2.2 The Balmer Jump . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
3.3 Planetary Ephemerides . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
4 Solving Differential Equations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
4.1 Numerical Integration of Initial Value Problems . . . . . . . . . . . . . . 105
4.1.1 First Order Differential Equations . . . . . . . . . . . . . . . . . . . 105
4.1.2 Second Order Differential Equations . . . . . . . . . . . . . . . . . 116
4.2 Radial Fall . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126
4.3 Orbital Mechanics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
4.4 Galaxy Collisions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
ix
x Contents
Since it depends on your personal preferences which software suits you best, we do
not presume a particular GUI or Python distribution here. If you choose to work with
Spyder or Jupyter, online documentation and tutorials will help you to install the soft-
ware and to get started (browse the official documentation under docs.spyder-ide.org
and jupyter-notebook.readthedocs.io/en/stable). For a comprehensive guideline, see
also [1, appendices A and B]. A powerful all-in-one solution is Anaconda, a Python
distribution and package manager that can be installed under Windows, macOS or
Linux by any user (see docs.anaconda.com for more information). Anaconda pro-
vides a largely autonomous environment with all required components and libraries
on a per-user basis. Of course, this comes at the cost of large resource consumption
(in particular, watch your available disk space).
As a first step, check if you can run the traditional “Hello, World!” example with
your favorite Python installation. Being astronomers, we use a slightly modified
version:
1 print("Hello, Universe!")
In this book Python source code is listed in frames with lines numbered on the left (in
the above example, there is just one line). Although these line numbers are not part
of the source code (that’s why they are shown outside of the frame), they are useful
for referring to particular parts of a code example. You might be able to display line
numbers in your code editor (in Jupyter notebooks, for example, line numbering can
be switched on and off in the View menu), but you should not confuse these numbers
with the line numbers used in this book. Usually we will continue the numbering
over several frames if the displayed pieces of code are related to each other, but we
also frequently reset line numbers to 1 when a new program starts or a new idea is
introduced. Whenever you encounter a code line with number 1 it should alert you:
at this point something new begins.
After executing the print statement in line 1 above, you should see somewhere on
your screen the output1
Hello, Universe!
The quotes in the source code are not shown in the output. They are used to signify
that the enclosed characters form a string. As you might have guessed, the print()
function puts the string specified in parentheses on the screen (more precisely, in a
window or frame that is used by Python for output).2
1 How to execute Python code depends on the software you are using (consult the documentation).
In a notebook, for example, all you need to do is to simultaneously press the enter and shift keys of
your keyboard in the cell containing the code.
2 Enclosing the string in parentheses is obligatory in Python 3. You may find versions of “Hello,
World!” without parentheses on the web, which work only with Python 2.
1.2 Understanding Expressions and Assignments 3
Apart from printing messages on the screen, which is not particularly exciting by
itself, Python can be used as a scientific calculator. Let us begin right away with an
example from astronomy. Suppose we want to calculate the velocity at which Earth
is moving along its orbit around the Sun. For simplicity, we treat the orbit as circular
(in fact, it is elliptical with a small eccentricity of 0.017). From the laws of circular
motion it follows that we can simply calculate the velocity as the circumference 2π r
of the orbit divided by the period P, which happens to be one year for Earth. After
having looked up the value of π, the orbital radius r (i.e. the distance to the Sun) in
km, and the length of a year in seconds,3 we type
1 2*3.14159*1.496e8/3.156e7
and, once evaluated by Python, we obtain
29.783388086185045
for the orbital velocity in km/s. Line 1 is an example for a Python expression con-
sisting of literal numbers and the arithmetic operators * and / for multiplication
and division, respectively. The factor of two in the formula for the circumference
is simply written as the integer 2, while the number π is approximately expressed
in fixed-point decimal notation as 3.14159.4 The radius r = 1.496 × 108 km is
expressed as 1.496e8, which is a so-called floating point literal . The character
e followed by an integer indicates the exponent of the leading digit in the decimal
system. In this case, e8 corresponds to the factor 108 . Negative exponents are indi-
cated by a minus sign after e. For example, 10−3 can be expressed as 1.0e-3 or
just 1e-3 (inserting + for positive exponents is optional).
Of course, there is much more to Python than evaluating literal expressions like
a calculator. To get an idea how this works, we turn the example shown above into a
little Python program:
1 radius = 1.496e8 # orbital radius in km
2 period = 3.156e7 # orbital period in s
3
4 # calculate orbital velocity
5 velocity = 2*3.14159*radius/period
Lines 1, 2, and 5 are examples for assignments. Each assignment binds the value of
the expression on the right-hand side of the equality sign = to a name on the left-hand
side. A value with a name that can be used to refer to that value is in essence what is
3 Strictlyspeaking, the time needed by Earth to complete one revolution around the Sun is the
sidereal year, which has about 365.256 d. One day has 86400 s.
4 In many programming languages, integers such as 2 are treated differently than floating point
numbers. For example, using 2.0 instead of the integer 2 in a division might produce a different
result. In Python 3, it is usually not necessary to make this distinction. Alas, Python 2 behaves
differently in this respect.
4 1 Python Basics
called a variable in Python.5 In line 5, the variables radius and period are used
to compute the orbital velocity of Earth from the formula
2π r
v= (1.1)
P
and the result is in turn assigned to the variable velocity. Any text between the
hash character # and the end of a line is not Python code but a comment explaining
the code to someone other than the programmer (once in a while, however, even
programmers might be grateful for being reminded in comments about code details
in long and complex programs). For example, the comments in line 1 and 2 provide
some information about the physical meaning (radius and period of an orbit) and
specify the units that are used (km and s). Line 4 comments on what is going on in
the following line.
Now, if you execute the code listed above, you might find it surprising that there
is no output whatsoever. Actually, the orbital velocity is computed by Python, but
assignments do not produce output. To see the value of the velocity, we can append
the print statement
6 print(velocity)
to the program (since this line depends on code lines 1–5 above, we continue the
numbering and will keep doing so until an entirely new program starts), which results
in the output6
29.783388086185045
This is the same value we obtained with the calculator example at the beginning of
this section.
However, we can do a lot better than that by using further Python features. First
of all, it is good practice to print the result of a program much in the same way as,
hopefully, you would write the result of a handwritten calculation: It should be stated
that the result is a velocity in units of km/s. This can be achieved quite easily by using
string literals as in the very first example in Sect. 1.1:
7 print("orbital velocity =", velocity, "km/s")
producing the output
5 The concept of a variable in Python is different from variables in programming languages such as
C, where variables have a fixed data type and can be declared without assigning a value. Basically, a
variable in C is a placeholder in memory whose size is determined by the data type. Python variables
are objects that are much more versatile.
6 In interactive Python, just writing the variable name in the final line of a cell would also result in
Optionally, the total number of digits in the formatting command can be omitted.
Python will then just fill in the leading digits before the decimal point (try it; also
change the figures in the command and try to understand what happens). A fixed
number of digits can be useful, for example, when printing tabulated data.
As the term variable indicates, the value of a variable can be changed in subsequent
lines of the program by assigning a new value. For example, you might want to
6 1 Python Basics
calculate the orbital velocity of a hypothetical planet at ten times the distance of
the Earth from the Sun, i.e. r = 1.496 × 109 km. To that end, we could start with
the assignment radius=1.496e9. Alternatively, we can make use of the of the
current value based on the assignment in line 1 and do the following:
9 radius = 10*radius
10 print("new orbital radius = {:.3e} km".format(radius))
Although an assignment may appear as the equivalent of a mathematical equality, it is
of crucial importance to understand that it is not. Transcribing line 9 into the algebraic
equation r = 10r is nonsense because one would obtain 1 = 10 after dividing through
r , which is obviously a contradiction. Keep in mind:
The assignment operator = in Python means set to, not is equal to.
The radius is displayed in exponential notation with three digits after the decimal
point, which is enabled by the formatting type e in place of f in the placeholder
{:.3e} for the radius (check what happens if you use type f in line 10). You must
also be aware that repeatedly executing the assignment radius = 10*radius
in interactive Python increases the radius again and again by a factor of 10, which is
possibly not what you might want. However, repeated operation on the same variable
is done on purpose in iterative constructions called loops (see Sect. 1.3).
After having defined a new radius, it would not be correct to go straight to the
computation of the orbital velocity since the period of the orbit changes, too. The
relation between period and radius is given by Kepler’s third law of planetary motion,
which will be covered in more detail in Sect. 2.2. For a planet in a circular orbit around
the Sun, this relation can be expressed as7
4π2 3
P2 = r , (1.2)
GM
7 Hereit is assumed that the mass of the planet is negligible compared to the mass of the Sun. For
the general formulation of Kepler’s third law see Sect. 2.2.
1.2 Understanding Expressions and Assignments 7
where M=1.989 × 1030 kg is the mass of the Sun and G=6.674 × 10−11 N kg−2 m2
is the gravitational constant. To calculate P for given r , we rewrite this equation in
the form
P = 2π (G M)−1/2 r 3/2 .
This formula can be easily turned into Python code by using the exponentiation
operator ** for calculating the power of an expression:
11 # calculate period in s from radius in km (Kepler’s third law)
12 period = 2*3.14159 * (6.674e-11*1.989e30)**(-1/2) * \
13 (1e3*radius)**(3/2)
14 # print period in yr
15 print("new orbital period = {:.1f} yr".format(period/3.156e7))
16
17 velocity = 2*3.14159*radius/period
18 print("new orbital velocity = {:.2f} km/s".format(velocity))
Hence, it would take more than thirty years for the planet to complete its orbit around
the Sun, as its orbital velocity is only about one third of Earth’s velocity. Actually,
these parameters are quite close to those of the planet Saturn in the solar system.
The backslash character \ in line 12 is used to continue an expression that does not
fit into a single line in the following line (there is no limitation on the length of a
line in Python, but code can become cumbersome to read if too much is squeezed
into a single line). An important lesson taught by the code listed above is that you
always need to be aware of physical units when performing numerical calculations.
Since the radius is specified in km, we obtain the orbital velocity in km/s. However,
the mass of the Sun and the gravitational constants in the expression for the orbital
period in lines 12–13 are defined in SI units. For the units to be compatible, we
need to convert the radius from km to m. This is the reason for the factor 103 in the
expression (1e3*radius)**(3/2). Of course, this does not change the value of
the variable radius itself. To avoid confusion, it is stated in the comment in line 11
which units are assumed. Another unit conversion is applied when the resulting period
is printed in units of a year in line 15, where the expression period/3.156e7 is
evaluated and inserted into the output string via format. As you may recall from
the beginning of this section, a year has 3.156 × 107 s.
Wrong unit conversion is a common source of error, which may have severe con-
sequences. A famous example is the loss of NASA’s Mars Climate Orbiter due to
the inconsistent use of metric and imperial units in the software of the spacecraft.8
As a result, more than $100 million were quite literally burned on Mars. It is there-
fore extremely important to be clear about the units of all physical quantities in a
8 See mars.jpl.nasa.gov/msp98/orbiter/.
8 1 Python Basics
program. Apart from the simple, but hardly foolproof approach of using explicit con-
version factors and indicating units in comments, you will learn different strategies
for ensuring the consistency of units in this book.
The computation of the orbital velocity of Earth in the previous section is a very
simple example for the implementation of a numerical algorithm in Python.9 It
involves the following steps:
1. Initialisation of all data needed to perform the following computation.
2. An exactly defined sequence of computational rules (usually based on mathe-
matical formulas), unambiguously producing a result in a finite number of steps
given the input from step 1.
3. Output of the result.
In our example, the definition of the variables radius and period provides the
input, the expression for the orbital velocity is a computational rule, and the result
assigned to the variable velocity is printed as output.
A common generalization of this simple scheme is the repeated execution of the
same computational rule in a sequence of steps, where the outcome of one step is
used as input for the next step. This is called iteration and will be explained in the
remainder of this section. The independent application of the same operations to
multiple elements of data is important when working with arrays, which will be
introduced in Chap. 2.
Iteration requires a control structure for repeating the execution of a block of
statements a given number of times or until a certain condition is met and the iteration
terminates. Such a structure is called a loop. For example, let us consider the problem
of summing up the first 100 natural numbers (this is a special case of an arithmetic
series, in which each term differs by the previous one by a constant):
n
sn ≡ k = 1 + 2 + 3 + ... + n. (1.3)
k=1
9 The term algorithm derives from the astronomer and mathematician al-Khwarizmi whose name
was transcribed to Algoritmi in Latin (cf. [2] if you are interested in the historical background).
al-Khwarizmi worked at the House of Wisdom, a famous library in Bagdad in the early 9th century.
Not only was he the founder of the branch of mathematics that became later known as algebra, he
also introduced the decimal system including the digit 0 in a book which was preserved until the
modern era only in a Latin translation under the title Algoritmi de numero Indorum (this refers to
the origin of the number zero in India). The digit 0 is quintessential to the binary system used on
all modern computers.
1.3 Control Structures 9
...
...
...
of the for loop with the
counter k, resulting in the k=99 sum=4851+99 = 4950
sum shown at the bottom.
The arrows indicate how k=100 sum=4950+100 = 5050
values from one iteration are
Result
used in the next iteration.
sum=5050
Values of the loop counter
are shown in red
where n = 100. In Python, we can perform the summation using a for loop:
1 sum = 0 # initialization
2 n = 100 # number of iterations
3
4 for k in range(1,n+1): # k running from 1 to n
5 sum = sum + k # iteration of sum
6
7 print("Sum =", sum)
The result is
Sum = 5050
The keywords for and in indicate that the loop counter k runs through all integers
defined by range(1,n+1), which means the sequence 1, 2, 3, . . . , n in mathe-
matical notation. It is a potential source of confusion that Python includes the start
value 1, but excludes the stop value n+1 in range(1,n+1).10
The indented block of code following the colon is executed subsequently for each
value of the loop counter. You need to be very careful about indentations in Python!
They must be identical for all statements in a block, i.e. you are not allowed to use
more or less white space or mix tabs and white spaces. We recommend to use one tab
per indentation. The block ends with the first non-indented statement. In the example
above, only line 5 is indented, so this line constitutes the body of the loop, which
adds the value of the loop counter to the variable sum. The initial value of sum must
be defined prior to the loop (see line 1). Figure 1.1 illustrates how the variables are
iterated in the loop.
10 Thereis a reason for the stop value being excluded. The default start value is 0 and range(n)
simply spans the n integers 0, 1, 2, . . . , n − 1.
10 1 Python Basics
Actually, our Python program computes the sum exactly in the way intended by
the teacher of nine-year old Carl Friedrich Gauss11 in school, just by summing up the
numbers from 1 to 100. However, Gauss realized that there is a completely different
solution to the problem and he came up with the correct answer much faster than
expected by his teacher, while his fellow students were still tediously adding up
numbers. The general formula discovered by Gauss is (the proof can be found in any
introductory calculus textbook or on the web):
n
n(n − 1)
sn = k= . (1.4)
k=1
2
We leave it as an exercise to check with Python that this formula yields the same
value as direct summation.
A slightly more demanding example is the calculation of the Fibonacci sequence12
using the recursion formula
The meaning of Eq. (1.5) is that any Fibonacci number is the sum of the two preceding
ones, starting from 0 and 1. The following Python program computes and prints the
Fibonacci numbers F1 , F2 , . . . , F10 (or as many as you like):
1 # how many numbers are computed
2 n_max = 10
3
4 # initialize variables
5 F_prev = 0 # 0. number
6 F = 1 # 1. number
7
8 # compute sequence of Fibonacci numbers
9 for n in range(1,n_max+1):
10 print("{:d}. Fibonacci number = {:d}".format(n,F))
11
12 # next number is sum of F and the previous number
13 F_next = F + F_prev
14
11 German mathematician, physicist, and astronomer who is known for the Gauss theorem, the
normal distribution, and many other import contributions to algebra, number theory, and geometry.
12 Named after Leonardo de Pisa, also known as Fibonacci, who introduced the sequence to European
mathematics in the early 13th century. However, the Fibonacci sequence was already known to
ancient Greeks. It was used to describe growth processes and there is a remarkable relation to the
golden ratio.
1.3 Control Structures 11
1. Fibonacci number = 1
2. Fibonacci number = 1
3. Fibonacci number = 2
4. Fibonacci number = 3
5. Fibonacci number = 5
6. Fibonacci number = 8
7. Fibonacci number = 13
8. Fibonacci number = 21
9. Fibonacci number = 34
10. Fibonacci number = 55
Since two variables are printed, we need two format fields where the values of the
variables are inserted (see line 10). As an alternative to using format(), the same
output can be produced by means of a formatted string literal (also called f-string)14 :
print(f"{n:d}. Fibonacci number = {F:d}")
Here, the variable names are put directly into the string. The curly braces indicate
that the values assigned to the names n and F are to be inserted in the format defined
after the colons (in this example, as integers with arbitrary number of digits). This is a
convenient shorthand notation. Nevertheless, we mostly use format() in this book
because the syntax maintains a clear distinction between variables and expressions
on the one hand and formatted strings on the other hand. If you are more inclined to
f-strings, make use of them as you please.
Suppose we would like to know all Fibonacci numbers smaller than, say, 1000.
We can formally write this as Fn < 1000. Since it is not obvious how many Fibonacci
numbers exist in this range, we need a control structure that repeats a block of code
13 As you can see from Fig. 1.2, F11 is computed as final value of F_next. But it is not used. You
can try to modify the program such that only 9 iterations are needed to print the Fibonacci sequence
up to F10 .
14 This feature was introduced with Python 3.6.
12 1 Python Basics
...
...
...
...
...
the next iteration, this
number is re-assigned to F, n=9 F_prev=21 F=34 F_next=21+34 = 55
and the value of F to
F_prev, as indicated by the n=10 F_prev=34 F=55 F_next=34+55 = 89
arrows
Result
F=55
1. Fibonacci number = 1
2. Fibonacci number = 1
3. Fibonacci number = 2
4. Fibonacci number = 3
5. Fibonacci number = 5
6. Fibonacci number = 8
7. Fibonacci number = 13
1.3 Control Structures 13
8. Fibonacci number = 21
9. Fibonacci number = 34
10. Fibonacci number = 55
11. Fibonacci number = 89
12. Fibonacci number = 144
13. Fibonacci number = 233
14. Fibonacci number = 377
15. Fibonacci number = 610
16. Fibonacci number = 987
Of course, the first ten numbers are identical to the numbers from our previous
example. If you make changes to a program, always check that you are able to
reproduce known results!
The loop header in line 6 of the above listing literally means: Perform the following
block of code while the value of F is smaller than 1000. The expression F<1000 is
an example of a Boolean (or logical) expression. The operator < compares the two
operands F and 1000 and evaluates to True if the numerical value of F is smaller
than 1000. Otherwise, the expression evaluates to False and the loop terminates.
Anything that is either True or False is said to be of Boolean type. In Python, it
is possible to define Boolean variables.
A while loop does not come with a counter. To keep track of how many Fibonacci
numbers are computed (in other words the index n of the sequence Fn ), we initialize
the counter n along with F in the multiple assignment in line 3. This is equivalent to
n = 1
F = 1
Python allows you to assign multiple values separated by commas to multiple vari-
ables (also separated by commas) in a single statement, where the ordering on the
left corresponds to the ordering on the right. We will make rarely use of this feature.
While it is useful in some cases (for example, to swap variables15 or for functions
returning multiple values), multiple assignments are rather difficult to read and prone
to errors, particularly if variables are interdependent.
While the loop counter of a for loop is automatically incremented, we need to
explicitly increase our counter in the example above at the end of each iteration. In
line 15, we use the operator += to increment n in steps of one, which is equivalent
to the assignment n=n+1 (there are similar operators -=, *=, etc. for the other basic
arithmetic operations).
Let us try to be even smarter and count how many even and odd Fibonacci numbers
below a given limit exist. This requires branching, i.e. one block of code will be
executed if some condition is met and an alternative block if not (such blocks are
also called clauses). This is exactly the meaning of the if and else statements
in the following example:
15Another application in our Fibonacci program would be the merging of lines 13 and 14 into the
multiple assignment F_prev,F = F,F_next.
14 1 Python Basics
1 # initialize variables
2 F_prev = 0 # 0. number
3 F = 1 # 1. number
4 n_even = 0
5 n_odd = 0
6
7 # compute sequence of Fibonacci numbers smaller than 1000
8 while F < 1000:
9 # next number is sum of F and the previous number
10 F_next = F + F_prev
11
12 # prepare next iteration
13 F_prev = F # first reset F_prev
14 F = F_next # then assign next number to F
15
16 # test if F is even (divisible by two) or odd
17 if F%2 == 0:
18 n_even += 1
19 else:
20 n_odd += 1
21
22 print("Found {:d} even and {:d} odd Fibonacci numbers".\
23 format(n_even,n_odd))
Instead of a single counter, we need two counters here, n_even for even Fibonacci
numbers and n_odd for the odd ones. The problem is to increment n_even if the
value of F is an even number. To that end the modulo operator % is applied to get
the remainder of division by two. If the remainder is zero, then the number is even.
This is tested with the comparison operator == in the Boolean expression following
the keyword if in line 17. If this expression evaluates to True, then the counter
for even numbers is incremented (line 18). If the condition is False, the else
branch is entered and the counter for odd numbers is incremented (line 20). You
must not confuse the operator ==, which compares variables or expressions without
changing them, with the assignment operator =, which sets the value of a variable.
The program reports (we do not bother to print the individual numbers again):
Altogether, there are 5 + 11 = 16 numbers. You may check that this is in agreement
with the listed numbers.
Python offers a collection of useful tools in the Python Standard Library (see
docs.python.org/3/library). Functions such as print() are part of the Standard
Library. They are called built-in functions. Apart from that, many more optional
1.4 Working with Modules and Objects 15
libraries (also called packages) are available. Depending on the Python distribution
you use, you will find that some libraries are included and can be imported as shown
below, while you might need to install others.16 Python libraries have a hierarchical
modular structure. This means that you do not necessarily have to load a complete
library, but you can access some part of a library, which can be a module, a submod-
ule (i.e. a module within a module) or even individual names defined in a module.
To get started, it will be sufficient to consider a module as a collection of definitions.
By importing a module, you can use variables, functions, and classes (see below)
defined in the module.
For example, important physical constants and conversion factors are defined
in the constants module of the SciPy library (for more information, see
www.scipy.org/about.html). A module can be loaded with the import command:
1 import scipy.constants
To view an alphabetically ordered list of all names defined in this module, you can
invoke dir(scipy.constants) (this works only after a module is imported).
By scrolling through the list, you might notice the entry
’gravitational_constant’. As the name suggests, this is the gravitational
constant G. Try
2 print(scipy.constants.gravitational_constant)
which displays the value of G in SI units:
6.67408e-11
16 See,
for example, packaging.python.org/tutorials/installing-packages
and docs.conda.io/projects/conda/en/latest/user-guide/tasks/manage-pkgs.html.
16 1 Python Basics
In this case, only G is imported, while in the examples above all names from
scipy.constants are made available. Importing names via the keyword from
should be used with care, because they can easily conflict with names used in assign-
ments elsewhere. Python does not treat this as an error. Consequently, you might
accidentally overwrite a module variable such as G with some other value.
By using constants from Python libraries, we can perform computations without
looking up physical constants on the web or in textbooks and inserting them as literals
in the code. Let us return to the example of a planet at 10 times the distance of Earth
from the Sun, i.e. r = 10 au (see Sect. 1.2). Here is an improved version of the code
for the computation of the orbital period and velocity:
1 from math import pi,sqrt
2 from astropy.constants import M_sun
3 from scipy.constants import G,au,year
4
5 print("1 au =", au, "m")
6 print("1 yr =", year, "s")
7
8 radius = 10*au
9 print("\nradial distance = {:.1f} au".format(radius/au))
10
11 # Kepler’s third law
12 period = 2*pi * sqrt(radius**3/(G*M_sun.value))
13 print("orbital period = {:.4f} yr".format(period/year))
14
15 velocity = 2*pi * radius/period # velocity in m/s
16 print("orbital velocity = {:.2f} km/s".format(1e-3*velocity))
1 au = 149597870691.0 m
1 yr = 31536000.0 s
We utilize the value of π and the square-root function defined in the math module,
which is part of the standard library. The function sqrt() imported from math is
called in line 12 with the expression radius**3/(G*M_sun.value) as argu-
ment. This means that the number resulting from the evaluation of this expression is
passed as input to sqrt(), which executes an algorithm to compute the square root
of that number. The result returned by the function and is then multiplied with 2*pi
to obtain the orbital period. Moreover, we use constants and conversion factors from
the SciPy and Astropy libraries. For instance, au is one astronomical unit in m and
year is one year in s. The values are printed in lines 5 and 6. These conversion
factors enable us to conveniently define the radius in astronomical units (line 8) and,
after apply Kepler’s third law in SI units, to print the resulting orbital period in years
(lines 12 and 13). When printing the radius in line 9, the newline character ’\n’
at the beginning of the string inserts a blank line. Other than in Sect. 1.2, the orbital
1.4 Working with Modules and Objects 17
This implies that G is also an object, albeit a rather simple one. If you print M_sun,
you will find quite a bit more information in there, such as the uncertainty of the
value and its physical unit:
Particular data items are called object attributes. For example, the value of the solar
mass is an attribute of M_sun. You can fetch an attribute by joining the names of the
object and the attribute with a dot. We refer to the attribute value in line 12 to obtain
a pure number that can be combined with other numbers in an arithmetic expression.
To list attributes belonging to an object, you can use dir(), just like for modules,
or search the documentation. Attributes and methods are defined in classes. While
objects belonging to the same class may contain different data, they have the same
attributes and methods. For example, M_earth from astropy.constants has
a value attribute just like M_sun, but the value behind this attribute is Earth’s mass
instead of the mass of the Sun. Both objects belong to the class Quantity. You can
take a glimpse behind the curtain in Appendix A, where you are briefly introduced
to writing your own classes.
Since everything in Python is an object, so is a string. Now you are able to better
understand the meaning of format() being a method. It is a method allowing you
to insert formatted numbers into a string.17 While methods are relatives of Python
functions, a method always has to be called in conjunction with a particular object.
In the print statements in lines 9, 13, and 16, the objects are string literals. The
17 Sincestrings are immutable objects, the method does not change the original string with place-
holders. It creates a new string object with the formatted numbers inserted.
18 1 Python Basics
syntax is similar to accessing object attributes, except for the arguments enclosed in
parentheses (here, the variables holding the numbers to be inserted). In general, you
can call methods on names referring to objects – in other words, Python variables.
This will be covered in more detail in the next chapter.
Chapter 2
Computing and Displaying Data
In astronomy, the positions of stars are specified by directions on the sky, i.e. two
angular coordinates. The radial distance of the star from Earth would be the third
coordinate, but distances are not known for all astronomical objects. As long as
distances do not matter, all astronomical objects can be projected in radial directions
onto a sphere with Earth at its center (the size of the sphere does not matter, but you
can think of it as a distant spherical surface). This is the so-called celestial sphere.1
For an observer on Earth, the position of astronomical objects depends on geo-
graphical latitude and longitude and varies with the time of day. To specify positions
independent of the observer, angular coordinates with respect to fixed reference direc-
tions are used. In the equatorial coordinate system, one reference direction is given
by Earth’s rotation axis (or, equivalently, the orientation of the equatorial plane). The
orientation of the rotation axis is fixed because of angular momentum conservation.2
The angular distance of a star from the equatorial plane is called declination and
denoted by δ. The other reference direction is defined by the intersection between
1 In ancient and medieval times, it was thought that there is actually a physical sphere with the
stars attached to it and Earth at its center. This is known as geocentric world view. The celestial
sphere in modern astronomy is merely a useful mathematical construction with no physical meaning
whatsoever.
2 This is not exactly true since other bodies in the solar system cause perturbations, but changes are
s outh
c e l e s t ial p o l e
the equatorial plane and the plane of Earth’s orbital motion around the Sun. The fixed
orientation of the orbital plane, which is called ecliptic, is also a consequence of angu-
lar momentum conservation (in this case the angular momentum of orbital motion).
The second coordinate in the equatorial system, which is called right ascension α, is
the angle measured from one of the two points where the celestial sphere is pierced
by the line of intersection of the equatorial an orbital planes. The zero point for the
right ascension is known as vernal equinox, the opposite point as autumnal equinox.
If all of this sounds rather complicated, it will become clear from the illustration in
Fig. 2.1. See also [3, Sect. 2.5].
While the declination of stars is constant, the position of the Sun changes in the
equatorial system over the period of a year. This is a consequence of the inclination
of Earth’s rotation axis with respect to the direction perpendicular to the ecliptic,
which is equal to 0 = 23.44◦ . The angle 0 is called obliquity of the ecliptic. The
annual variation of the declination of the Sun is approximately given by3
360◦
δ = − arcsin sin 0 cos (N + 10) (2.1)
365.24
3 See en.wikipedia.org/wiki/Position_of_the_Sun.
2.1 Spherical Astronomy 21
where N is the difference in days starting from 1st January. So the first day of the year
corresponds to N = 0, and the last to N = 364 (unless it is a leap year). The fraction
360◦ /365.24 equals the change in the angular position of Earth per day, assuming a
circular orbit. This is just the angular velocity ω of Earth’s orbital motion around the
Sun in units of degrees per day.4
The Sun has zero declination at the equinoxes (intersection points of celestial
equator and ecliptic) and reaches ±0 at the solstices, where the rotation axis of
the Earth is inclined towards or away from the Sun. The exact dates vary somewhat
from year to year. In 2020, for instance, equinoxes were on 20th March and 22nd
September and solstices on 20th June and 21st December (we neglect the exact times
in the following). In Exercise 2.2 you are asked to determine the corresponding values
of N . For example, the 20th of June is the 172nd day of the year 2020. Counting
from zero, we thus expect the maximum of the declination δ = 0 (first solstice) for
N = 171. Let us see if this is consistent with the approximation (2.1). The following
Python code computes the declination δ based on this formula for a given value of N :
1 import math
2
3 N = 171 # day of 1st solstice
4 omega = 2*math.pi/365.24 # angular velocity in rad/day
5 ecl = math.radians(23.44) # obliquity of the ecliptic
6
7 # approximate expression for declination of the Sun
8 delta = -math.asin(math.sin(ecl)*math.cos(omega*(N+10)))
9 print("declination = {:.2f} deg".format(math.degrees(delta)))
The result
is close to the expected value of 23.44◦ . To implement Eq. (2.1), we use the sine,
cosine, and arcsine functions from the math library. The arguments of these
functions must be specified in radians. While the angular velocity is simply given
by 2π/365.24 rad/d (see line 4), 0 is converted into radians with the help of
math.radians() in line 5. Both values are assigned to variables, which allows
us to reuse them in subsequent parts of the program. Since the inverse function
math.asin() returns an angle in radians, we need to convert delta into degrees
when printing the result in line 9 (‘deg’ is short for degrees).
To calculate the declination for the second solstice and also for the equinoxes,
we need to evaluate the code in line 8 for the corresponding values of N . If you
put the code listed above into a Python script, you can change the value assigned to
the variable N and simply re-run the script. While this is certainly doable for a few
different values, it would become too tedious for many values (we will get to that
soon enough). Ideally, we would like to compute the Sun’s declination for several
4 The angular velocity in radians per unit time appears as factor 2π/P in Eq. (1.1).
22 2 Computing and Displaying Data
days at once. This can be done by using data structures know as arrays. An array is
an ordered collection of data elements of the same type. Here is as an example:
10 import numpy as np
11
12 # equinoxes and solstices in 2020
13 N = np.array([79, 171, 265, 355])
In contrast to other programming languages, arrays are not native to Python. They
are defined in the module numpy (the library name is also written as NumPy, see
www.numpy.org), which is imported under the alias np in line 10. The function
np.array() takes a list of values in brackets and, if possible, creates an array
with these values as elements. Like an array, a list is also an ordered collection
of data elements. In this book, we will rarely make use of lists (see, for example,
Sect. 5.5). They are more flexible than NumPy arrays, but flexibility comes at the
cost of efficiency. This is demonstrated in more detail in Appendix B.1. On top
of that, NumPy offers a large toolbox of numerical methods which are specifically
implemented to work with arrays.5 The array returned by np.array() is assigned
to the variable N (mark the difference between single value versus array in lines 3
and 13, respectively). Its main properties can be displayed by the following print
statements:
14 print(N)
15 print(N.size)
16 print(N.dtype)
which produces the output
From this we see that N has four elements (the number of elements is obtained with
the .size attribute), which are the integers 79, 171, 256, and 355. The data type
can be displayed with the .dtype attribute (by default, 64-bit integers are used for
literals without decimal point).
How can we work with the values in an array? For example, N[0] refers to the
first element of the array, which is the number 79. Referring to a single element of
an array via an integer in brackets, which specifies the position of the element in the
array, is called indexing. The first element has index 0, the second element index 1
and so on (remember, this is Python’s way of counting). You can also index elements
from the end of the array. The last element has index −1, the element before the last
one has index −2, etc. So, for the array defined above,
17 print(N[1])
18 print(N[-3])
outputs
171
171
Do you see why the value 171 is printed in both cases? Vary the indices and see for
yourself what you get. Before proceeding, let us summarize the defining properties
of an array:
Every element of an array must have the same data type. Each element is
identified by its index.
Having defined N as an array, we could calculate the declination of the Sun on day
171 (the first solstice) by copying the code from line 8 and replacing N by N[1]in the
expression for delta. Of course, this would not bring us any closer to calculating
the declination of the Sun for all four days at once. With NumPy, it can be done as
follows.
19 delta = -np.arcsin(math.sin(ecl) * np.cos(omega*(N+10)))
20 print(np.degrees(delta))
In short, the expression in line 19 is evaluated for each element of the array N and
the results are stored in a new array that is assigned to delta. The four elements of
delta are the values of the declination for the two equinoxes and solstices:
Not perfect (the declination at equinoxes should be zero), but reasonably close. We
use only an approximate formula after all.
To get a better idea of how the code in line 19 works, we expand it into several
steps and use a temporary array called tmp for intermediate results of the calculation:
34 print(tmp)
35
36 # calculate the negative arcsine of each element
37 delta = -np.arcsin(tmp)
38 print(np.degrees(delta))
The next step is an element-wise multiplication with omega (Earth’s angular velocity
defined in line 4). Since the value of omega is a floating point number, the tmp array
is automatically converted from data type integer to float:
Now we have the angular positions of Earth on its orbit, from which the vertical
distances of the Sun from the celestial equator can be computed. In line 33, the
NumPy function np.cos() is used to compute the cosine of each value in the tmp
array, while math.sin() can take only a single-valued argument, which is ecl
(the obliquity of the ecliptic). The product is
and, after computing the arcsine of each element with np.arcsin, we finally get
the declinations:
While doing things step by step is helpful for beginners, combining all these steps
into the single statement shown in line 19 is extremely useful for the more experienced
programmer. Starting with the code for a single day (see line 8), the only modification
that has to be made is that the math module has to be replaced by numpy whenever
the argument of a function is an array. However, there is small pitfall. By comparing
the code examples carefully, you might notice that the identifiers for the arcsine
function read math.asin() and np.arcsin() in lines 8 and 19, respectively.
2.1 Spherical Astronomy 25
...
...
...
...
a[n-1] = b[n-1] + c a[n-1] = b[n-1] + c[n-1]
Fig. 2.2 Illustration of basic NumPy operations. Adding a number (variable) to an array, means
that the same number is added to each element in the array (left). If two arrays of size n are added,
the operator + is applied element-wise (right)
Since math and numpy are independent modules, you cannot expect that the naming
of mathematical functions is always consistent.
Printing an array, results in all elements of the array being displayed in some
default format. Now, suppose we want to format the elements nicely as we did at the
beginning of this section before introducing arrays. Formatted printing of a particular
element of an array is of course possible by indexing the element. For example,
39 print("declination = {:.2f} deg".
40 format(math.degrees(delta[1])))
produces the same output as line 9 (where delta is a simple variable):
To display all elements in a formatted table, we need to loop through the array.
Actually, we have already worked with such loops, for example, in line 19. Loops of
this type are called implicit. The following example shows an explicit for loop:
41 for val in delta:
42 print("declination = {:6.2f} deg".
43 format(math.degrees(val)))
The loop starts with the first element in the array delta, assigns its value to the
variable val, which is the loop variable (similar to the loop counter introduced in
Sect. 1.3), and then executes the loop body. The loop in the example above encom-
passes only a single print statement. After executing this statement, the loop contin-
ues with the next element and so on until the end of the array (the highest index) is
reached:
26 2 Computing and Displaying Data
However, the output is not really satisfactory yet. While repeatedly printing
declination is redundant, important information for understanding the data is
missing. In particular, the days to which these values refer are not specified. A bet-
ter way of printing related data in arrays is, of course, a table. In order to print the
days and the corresponding declinations, we need to simultaneously iterate through
the elements of N and delta. One solution is to define a counter with the help of
Python’s enumerate() function:
44 print("i day delta [deg]")
45 for i,val in enumerate(delta):
46 print("{1:d} {2:3d} {0:8.2f}".\
47 format(math.degrees(val),i,N[i]))
Compared to the for loops discussed in Sect. 1.3, the range of the counter i is
implicitly given by the size of an array: It counts through all elements of delta
and, at the same time, enables us to reference the elements of N sequentially in the
loop body. Execution of the loop produces the following table:
Here, the arguments of format() are explicitly ordered. The placeholder {1:d} in
line 46 indicates that the second argument (index 1 before the colon) is to be inserted
as integer at this position. The next placeholder refers to the third argument (index 2
before the colon) and the last one to the first argument. The format specifiers ensure
sufficient space between the columns of numbers (experiment with the settings). The
header with labels for the different columns is printed in line 44 (obviously, this has
to be done only once before the loop begins).
Surely, instead of enumerating delta, you could just as well enumerate N. Is
it possible to write a loop that simultaneously iterates both arrays without using
an explicit counter? Actually, this is the purpose of the zip() function, which
aggregates elements with the same index from two or more arrays of equal length.
In this case, the loop variable is a tuple containing one element from each array.
We will take a closer look at tuples below. All you need to know for the moment is
that the tuple row in the following code example contains one element of N and the
corresponding element of delta, forming one row of the table we want to print.
Tuple elements are indexed like array elements, so row[0] refers to the day and
row[1] to the declination for that day.
2.1 Spherical Astronomy 27
If you replace lines 50–51 by the unformatted print statement print(row), you
will get:
(79, -0.01580409076383853)
(171, 0.40893682550286947)
(265, -0.007321783769611206)
(355, -0.4091014813515704)
While brackets are used for lists and arrays, tuples are enclosed by parentheses.
The most important difference is that tuples are immutable, i.e. it is not possible
to add, change or remove elements. For example, the tuples shown above are fixed
pairs of numbers, similar to coordinates (x, y) for points in a plane. Suppose you
want to change day 355 to 364. While it is possible to modify array N by setting
N[3] = 364, you are not allowed to assign a value to an individual element in a
tuple, such as row[0]. You can only overwrite the whole tuple by a new one (this
is what happens in the loop above).
6 See [3, Sect. 2.6] if you are interested in how this equation comes about.
28 2 Computing and Displaying Data
le
po
est rth
ial
cel no
ce
les
tial
mer
hrise E
r
ato
idian
qu
le
tia
N S
ls
ce
hset
W
le
po
s ti o u th
s
al
le
ce
Fig. 2.3 Diurnal arc of a star moving around the celestial sphere (thick red circle) in the horizontal
system (see Sect. 2.1.3) of an observer at latitude φ (the horizontal plane is shown in grey). Since the
equatorial plane is inclined by the angle 90◦ − φ against the horizontal plane, the upper culmination
of the star at the meridian is given by amax = 90◦ − φ + δ, where δ is the declination. In the co-
rotating system, the star rises at hour angle h rise , reaches its highest altitude when it crosses the
meridian at h = 0, and sets at the horizon at h set = −h rise
where δ is the declination of the object (see Sect. 2.1) and φ the latitude of the
observer’s position on Earth. As a consequence, the variable T = 2h set measures the
so-called sidereal time for which the object is in principle visible on the sky (stars
are of course outshined by the Sun during daytime). It is also known as length of the
diurnal arc.
For example, let us consider the star Betelgeuse in the constellation of Orion.
It is a red giant that is among the brightest stars on the sky. Its declination can be
readily found with the help of astropy.coordinates, which offers a function
that searches the name of an object in online databases:
1 from astropy.coordinates import SkyCoord, EarthLocation
2
3 betelgeuse = SkyCoord.from_name(’Betelgeuse’)
4 print(betelgeuse)
When you are confronted with the output for the first time, it might require a little
bit of deciphering:
This tells us that the right ascension (ra) and declination (dec) of the object named
Betelgeuse were found to be 88.79◦ and 7.41◦ , respectively.7 The variable
betelgeuse defined in line 3 represents not only an astronomical object; it is
a Python object, more specifically an object belonging to the class SkyCoord (see
Sect. 1.4 for objects in a nutshell). The attribute dec allows us to directly reference
the declination:
5 delta = betelgeuse.dec
6 print(delta)
The declination is conveniently printed in degrees (d), arc minutes (m) and arc sec-
onds (s), which is the preferred format to express angular coordinates in astronomy:
7d24m25.4304s
that an angle of 360◦ (one full rotation of Earth) corresponds to a sidereal day,
which is about 4 min shorter than a solar day. As explained in [3, Sect. 2.13], this
is a consequence of the orbital motion of Earth. The conversion is made easy by the
units module (see also Exercise 2.5 for a poor man’s calculation):
18 T = (math.degrees(2*h)/360)*u.sday
19 print("T = {:.2f}".format(T.to(u.h)))
First T is defined in sidereal days (u.sday), which is equivalent to 24h or 360◦ .
Then we convert to solar hours (u.h) by applying the method to() in line 19. The
result is
T = 13.31 h
If it were not for the Sun, Betelgeuse could be seen 13 h at the Observatory. Of course,
the star will be visible only during the overlap between this period and the night,
which depends on the date. We will return to this question in the following section.
The diurnal arc of the Sun plays a central role in our daily life, as it determines the
period of daylight. In Sect. 2.1.1, we introduced an approximation for the declination
δ of the Sun. By substituting the expression (2.1) for δ into Eq. (2.2), we can
compute how the day length varies over the year. First we need to compute δ for N
ranging from 0 to 364. Using NumPy, this is very easy. In the following example, the
expression np.arange(365) fills an array with the sequence of integers starting
from 0 up to the largest number smaller than 365, which is 364.9 Apart from that, the
code works analogous to the NumPy-based computation for equinoxes and solstices
in Sect. 2.1.1. Since a new task begins here, the line numbering is reset to 1, although
we make use of previous assignments (an example is the latitude phi). In other
words, you would need to add pieces from above to make the following code work
as an autonomous program (you might want to try this).
1 import numpy as np
2
3 N = np.arange(365) # array with elements 0,1,2,...,364
4 omega = 2*math.pi/365.24 # Earth’s angular velocity in rad/day
5 ecl = math.radians(23.44) # obliquity of the ecliptic
6
7 # calculate declination of the Sun for all days of the year
8 delta = -np.arcsin(math.sin(ecl) * np.cos(omega*(N+10)))
Now we can compute the day length T for all values in the array delta using
functions from the numpy module (compare to the code example for Betelgeuse and
check which changes have been made):
9 Just
like range() in for loops, the general form is np.arange(start, stop, step),
where a start value different from 0 and a step other than 1 can be specified as optional arguments.
Another Random Scribd Document
with Unrelated Content
var en anden af Familjens Venner. Han var Droskekører og boede i
Store Brøndstræde og havde i en Aarrække ved saadanne
Lejligheder beredvillig stillet sig selv og sit Køretøj til de Gamles
Raadighed.
Paa Slaget tre holdt han ogsaa dennegang udenfor Huset med sin
nedslaaede Vogn saa fin og velpudset, som var den bestemt for et
Grossererbryllup i Vor Frue. Efter nogen Venten aabenbarede det
alderstegne Ægtepar sig for den halve Snes af Kvarterets Børn, der
havde samlet sig ved Vognen, og for de mange flere Ældre, der fra
Døre og Vinduer var Tilskuere til dette Triumftog. Madam Olufsen var
i Wienersjal og bar en stor, lysviolet Vindrueklase paa Hatten,
Højbaadsmanden var i sin stiveste Begravelsespuds med 25-Aars
Tjenestemedaljen og Sølvkorset glimtende frem bag den uknappede
Overfrakke.
Først gjorde de en Rundtur gennem hele den gamle By, saae paa
de store, nye Bygninger, der skød op allevegne, paa Resterne af
Fæstningsvoldene, som man var i Færd med at sløjfe, og paa de
nye, overbyggede Omnibusser, der kom ind fra Frederiksberg, og
som i Østergades Menneskemylr lignede Elefanter med Ryttere paa
Ryggen. Fra Kongens Nytorv drejede de saa ned imod Kanalen,
standsede et Øjeblik udenfor Holmens Kirke, hvor de for 52 Aar
siden havde haft Bryllup, og havnede omsider paa Børsbryggen.
Snart sad man da bænket nede i det hyggelige lille Skafferum ved
et fuldt opdækket Bord, som hurtig fik Bugt med al forstilt
Beskedenhed.
Per følte sig stadig vel tilpas i saadant folkeligt Selskab. Ligesom
han aldrig spiste med friskere Appetit end ved et Bord som dette
med solide Flæskevarer og Snaps og Øl, fandt han endnu sin bedste
Underholdning i jævne Borgerfolks muntre og ligefremme Tale. Her
var han ikke — som i „Gryden“ — en tavs og kritisk Iagttager; han
kastede sig med Liv ind i Diskussionerne og snakkede med om Vejret
og Torvepriserne, om Færgevæsnet og Kommandantskabet.
Per fulgte dem til Holmens Bro, hvor han fik dem anbragt i en
Sporvogn. Selv vilde han ikke hjem. Han trængte til at lufte sig lidt
ovenpaa den megen Latter og de mange stærke Drikke. Saa
spejlede han sig et Øjeblik i et Butiksvindu og gik langsmed Kanalen
op mod Højbro.
Han blev rød af Skam. Saa ynkelig var han kommen tilkort den
første Gang, Livet for Alvor havde prøvet hans Tro og hans Mod.
— Men lod den skete Skade sig ikke genoprette? Om han nu f.
Eks. skrev et Brev til hende, forklarede hende alt og gjorde en
Undskyldning? — —
„Det er den Herre, der ogsaa var her igaar, … det er vist en
Præst. Han har siddet derinde over en Time.“
Per smilte. Og skønt han i Grunden ikke selv følte nogen Trang til
at drikke, hentede han en Flaske Øl i sit Hjørneskab og trak den op.
„Er det for at sige mig det, at du er kommen her?“ begyndte Per
straks i stridbar Tone.
Eberhard gjorde en ganske lille, afvisende Bevægelse med
Haanden.
Per vilde endnu ikke spørge, — vovede det paa en Maade heller
ikke. Han var overrasket over sig selv, forstod ikke dette stærke
Indtryk, som den blotte Anelse om et Ulykkesbudskab fra Hjemmet
gjorde paa ham. Han havde troet forlængst at være kommen ud
over den Slags Fornemmelser. De derhjemme havde jo i de senere
Aar været som døde for ham allesammen, og det var langt fra, at
Broderens Nærværelse vakte nogen Hjemlængsel hos ham.
Tværtimod. Som Eberhard sad der med Hænderne over sin Paraply
og iagttog ham fra Siden med sine Gedebukkeøjne, optændte han
paany alle Fortidens uforsonlige Følelser hos ham. Denne ophøjede
Anklage i Holdning og Miner, denne tavse Tilkendegivelse af krænket
Familjeære, den hele Selvgodhedens beklumrede Atmosfære, der
slog ham imøde fra Eberhards tilknappede Skikkelse, førte Mindet
om Barndomstidens Trængsler saa levende tilbage, at det var, som
om selve den forhadte Tørverøgsfims derhjemme fra
Præstegaardens Stuer bragtes ind til ham med Broderens Person.
„Aa nej, det kan jeg just ikke sige, Fader har skrantet ikke saa lidt
i den sidste Tid.“
„Ja saa.“
„Af hvad?“
„Hvad jeg vilde sige … jeg havde, forinden jeg rejste tilbage, en
længere Samtale med Doktor Carlsen, og jeg fik derved bekræftet,
hvad jeg allerede i længere Tid havde haft Fornemmelsen af gennem
Brevene derhjemme fra, at Faders Tilstand giver Anledning til de
alvorligste Bekymringer. Jeg tror, kort sagt, vi maa være forberedt
paa, at han inden ret lang Tid gaar bort.“
Per, som følte Broderens Øjne hvile aarvaagent paa ham, fortrak
ikke en Mine, skønt Hjertet hamrede i hans Bryst. Det var dog ikke
Sorg, han følte ved Budskabet, ikke engang Vemod, ikke heller
Anger. Den Uro, der greb ham, udsprang væsenlig fra en ham ikke
helt bevidst Skuffelse. Aldrig var den Tanke falden ham ind, at
Faderen eller Moderen kunde dø, før han ved sit Livsværk og sin Sejr
havde retfærdiggjort sig for dem. Og nu skulde denne Efterretning
endda træffe ham i et Øjeblik, da hans store Forhaabninger var
bleven slaaet saa beskæmmende til Jorden.
„Jeg forstaar ikke … Hvorledes mener du?“ spurgte Per brysk men
kunde dog ikke faa sig til at se over paa Broderen.
„Ja, jeg vil — som jeg før sagde — slet ikke blande mig i dine
Anliggender. Det skulde kun være en Henstilling til dig — du maa nu
afgøre med din egen Samvittighed, om du tror at kunne forsvare det
Forhold, hvori du saa længe har stillet dig til dine Forældre … jeg
ønsker ikke engang at udtale mig nærmere derom. Hvad jeg
derimod føler mig forpligtet til allerede nu at præcisere for dig, er, at
Faders Bortgang jo vil faa en væsenlig Indflydelse ogsaa paa de
pekuniære Forhold derhjemme. Jeg veed, at Fader hidtil — uden
iøvrig fra din Side at have mødt nogen Paaskønnelse derfor — har
ydet dig en regelmæssig Hjælp, som maaske ikke har været netop
rigelig, men med hvilken han dog — jeg kan sige det med Sikkerhed
— har strakt sig endog udover sine Kræfter. Og han har gjort det, for
at han ikke skulde kunne bebrejde sig at have vist Ligegyldighed for
dine Studier — eller hvad man nu skal kalde det — til Trods for, at
han ikke har været i Stand til at bedømme dine Evner eller, hvad
Fremskridt du har gjort.“
Eberhard rejste sig. Han var bleg, og hans Mund med det
fremtrædende Underbid var stiv af Ophidselse.
„Ja, jeg ser, at det i det hele er ørkesløst at tale med dig.
Jeg tror det rigtigst, at vi ikke fortsætter.“
„Som du vil.“
Han tog sin Hat for at gaa. Men da han stod ved Døren, vendte
han sig endnu engang om imod Per, der var bleven siddende ved
Bordet, og sagde:
„Jeg maa dog sige dig endnu et, Peter Andreas! Skønt du — med
dine Følelser — rimeligvis vil have vanskelig ved at forstaa det, skal
du vide, at der er ingen, paa hvem Fader i denne Tid tænker mere
end paa dig. Nu, da jeg var hjemme, gik der ikke en Dag, uden at
han talte til mig om dig … og Moder for den Sags Skyld ogsaa. De
har jo for længe siden maattet opgive at forsøge at indvirke paa dig
ved Overtalelser. De har maattet haabe paa, at Livet engang vilde
bøje dit Sind og lære dig, hvad du skylder dem. Nu er Faders Tid
rimeligvis snart omme. Vogt dig, Peter Andreas, for at begaa en
Synd, som du sikkert — tidlig eller sent — bittert vilde komme til at
angre.“
Efter at Broderen var gaaet, blev Per nogen Tid siddende med
Haanden under Kinden og saae mørkt ned for sig.
Han drak ud af sit Glas. Saa rejste han sig med et Sæt, som om
han rystede en ond Drøm af sig, og gik op til de Gamle for at snakke
sit Sind i Ligevægt.
Femte Kapitel.
Sit Forhold til den neergaardske Arv fik han nu hurtig ordnet uden
fornyede Skrupler. Han sagde til sig selv, at det dog ikke nyttede
med for megen Finfølelse. Man maatte gøre sig haard overfor den
Slags Fornemmelser, naar man skulde frem i Verden med et Par bare
Næver. Forresten viste det sig, at Summen ikke var saa stor som
ventet. Ved hans Besøg hos Sagføreren meddelte denne ham, at
Boet „var en Del belastet“, og med denne Erklæring lod han sig nøje
uden at tænke paa at foretage nogen nærmere Undersøgelse. Et Par
Tusinde Kroner maatte der efter Sagførerens Mening dog sikkert
blive til Rest, — det vilde altsaa sige, at han foreløbig havde sikret
sig Arbejdsro i det mindste et Aar. Et Forskud fik han straks og
meget beredvilligt udbetalt, saa han kunde afgøre sin Gæld.
Fru Engelhardt saae han ikke tiere. Han tænkte vel endnu
undertiden paa Muligheden af en Forsoning; men noget Skridt dertil
havde han ikke gjort. Hvor flov han end vedblev at være over sin
Optræden hin Nat, havde Oplevelsen alligevel efterladt en lille Mistro
hos ham til det galante Eventyrs saa højt priste Glæder. Han havde
spurgt sig selv, om de nu ogsaa virkelig var de mange dermed
forbundne Besværligheder, det meget Komediespil og navnlig den
store Bekostning værd. Hvergang Fristelsen til at forny
Bekendtskabet med den forfarne Dame kom over ham, behøvede
han blot at tænke paa den syndige Hoben Penge, hun havde kostet
ham bare paa en Aften, — og det faldt ham da i Almindelighed ikke
svært at glemme hende for sine Kanalprofiler og
Vandstandsberegninger.
Naar Solen skinnede, stod hans Vinduer gerne aabne ud til det Fri.
Sommerfugle og Humlebier forvildede sig da ind til ham ude fra
Haven, uden at dette Syn dog stemte ham lyrisk. I det højeste
kunde han give sig til at fløjte under Arbejdet, og da stak
Højbaadsmanden undertiden sit kalotprydede Hoved ind ad Vinduet
for at udtrykke sin Glæde over hans gode Humør, eller Madam
Olufsen stillede en Kop rygende Kaffe ind paa Karmen og bad ham
dog „unde sig Stunder til at trække Vejret“. Havde den gode Kone en
Tid frygtet for, at hendes Logerende var kommen alvorlig paa Afveje,
saa gik hendes Bekymringer nu snarest i modsat Retning.
„Tag nu og drik den Kaffetaar, mens den er varm,“ kunde hun sige
i den kommanderende Tone, hvormed hun gerne skjulte sine
moderlige Følelser for ham.
Det varede dog sjelden længe, før han fik Uro paa sig. Og atter
sad han bøjet over sine Tegninger og saae i Aanden Hakker og
Skovle blinke i Solen, saae, hvordan Høje jævnedes og Moser og
Kær fyldtes, og hørte det dumpe Drøn af Miner, der rystede selve
Jordens Grundvold ved et Tryk af hans Finger. Han havde igen paa
forskellig Maade omformet og udvidet sit Projekt. Saaledes havde
han — i nøje Tilknytning til Kanalnettet — udkastet Grundplanen til
en stor, ny Havn paa Jyllands Vestkyst, en Verdenshavn, der skulde
kunne optage Kampen med Hamborg og Bremen. Og ikke nok
dermed. Under sin Syslen med denne Opgave var han bleven ført
ind paa den Ide at udløse Vesterhavsbølgernes Energimængder ved
Hjælp af store Bøjer af sammennittede Jernplader, der skulde
udlægges i Brændingen, og hvorfra Kraften igen førtes gennem
Ledninger ind til industrielle Anlæg paa Stranden. Ogsaa Vindens
Arbejdskraft tænkte han sig udnyttet gennem Motorer, der skulde
være i Stand til at opsamle og ligesom nedpakke Energien, hvorved
der vilde være tilvejebragt Betingelser for at omskabe hele Landet til
et Fabriksland af allerførste Rang.
En saadan Musikaften fik Per Øje paa en nydelig ung Pige, der
stod bag et aabent Salsvindue i et af Nabohusene. Hun havde lagt
begge Armene paa Ryggen, tilsyneladende ganske optaget af at
høre paa Koncerten og samtidig betragte Aftenhimlens drivende
Skyer. Dog, Kindernes stadig tiltagende Rødme forraadte, at hun ikke
var helt uvidende om det Par dristige Mandfolkeøjne, der iagttog
hende ovre fra Højbaadsmandens Lysthus.
Huset derinde tjente som Embedsbolig for en af Nyboders
Rangspersoner, Mester Jacobæus, hvis Kone kaldtes Frue i hvert
Fald af Mandens Underordnede. Per fik bagefter at vide af Madam
Olufsen, at den unge Pige var denne Mands Broderdatter, og at
hun nylig var kommen her til Byen for at lære Skrædersyning.
Noget Blik blev der i det hele ikke vekslet imellem dem, hvor
meget Per end anstrengte sig for at aabne det stumme Sprog en
Telegrafforbindelse over Plankeværket. Saa en Morgenstund, da han
kom ud af Gadedøren, saae han hende første Gang udenfor Huset.
Hun skraaede netop over Gaden fra Bagerboden i Genbostedet —
iført grønne Plydses Hjemmesko og med en Hankekurv i Haanden.
Han kunde ikke lade være med at smile, da han saae, hvor ulykkelig,
ja næsten forbitret hun blev over, at han skulde møde hende under
saa beskæmmede Omstændigheder; denne Skamfuldhed gjorde
hende kun endnu mere indtagende i hans Øjne, og han bestemte sig
til at løfte paa Hatten. Hun lod imidlertid, som hun ikke saae ham;
og endnu samme Dags Eftermiddag skaffede hun sig en glansfuld
Oprejsning. Per vendte just hjem fra en af sine smaa raske
Spasereture paa Langelinje, da hun kom ud af Farbroderens Dør i en
fiks, lys Foraarsjaket, med en stor Silkesløjfe under Hagen og
slørombølget Hat. Et Øjeblik blev hun staaende paa Dørflisen for at
knappe den sidste Knap i et Par skinnende nye, sorte Handsker;
hvorpaa hun langsomt, med begge Hænder i Jaketlommerne, gik
ned imod Volden — uden at have saa meget som kastet et Strejfblik
til den Side, hvorfra Per kom. Men ogsaa dennegang maatte Per
smile. Han havde nemlig forinden skimtet hendes Ansigt oppe i
Mester Jacobæus’ Gadespejl, og han gissede nu, at hun havde set
ham gaa ud, og at hun havde staaet deroppe i al sin Puds for at
vente paa hans Tilbagekomst.
Men Per havde nu faaet Blod paa Tanden, og han besluttede sig til
en dristigere Tilnærmelse. Han lod Trine skaffe ham Underretning
om Beliggenheden af den Systue, hvor hun undervistes, og hvornaar
hun plejede at gaa derfra, — og en Eftermiddag ved Syvtiden
overraskede han hende ved Nørrevold, bedst som hun stod og saae
ind ad et Butiksvindu.
Han hilste hende med stor Ærbødighed og udbad sig Tilladelse til
at forestille sig for hende, og til hans Forundring viste hun sig ikke
fornærmet over hans Paatrængenhed. Det saae ud, som om hun
med provinsiel Enfoldighed fandt det ganske naturligt, at to Naboer,
der mødtes i den store By, gav sig i Samtale og gjorde hinanden
Følgeskab. Helt oprigtig var denne Troskyldighed imidlertid ikke. Hun
røbede det selv, da de nærmede sig Nyboder, idet hun pludselig
standsede og erklærede, at han nu ikke maatte følge hende
længere. Og Per, som vidste, at Mester Jacobæus var en nidkær
Mand, der var sig Ansvaret for den unge Broderdatter fuldt bevidst,
forlangte da heller ikke nogen Forklaring men tog Afsked med
Ønsket om et „snarligt Gensyn“.
I den følgende Tid mødtes de nu hyppig paa samme Maade og
fulgtes et Stykke hjemad. Ved en stiltiende Overenskomst lagde de
dog af Forsigtighedshensyn Vejen om ad Kongens Have og
Rosenborg-Gartnerierne, hvor de var mindre udsat for at møde
Nybodersfolk; og desuden forlængede Per hver Gang Vejen med et
lille Stykke, uden at hun gjorde Indsigelse derimod.
Hendes tilsyneladende saa dristige Forhold til Per skyldtes just den
samme skjulte Frygt for at blive anset for et Guds Ord fra Landet. Og
Per mistydede ikke denne Frimodighed, dertil var den for nær
beslægtet med hans egen jyske Selvhævdelsestrang.
Dagen efter indfandt Mester Jacobæus sig hos Per og spurgte ham
kort, endog uden først at sige, hvem han var, om det var hans
Hensigt at gifte sig med hans Broderdatter. Per prøvede i
Begyndelsen at snakke sig fra Sagen, bad ham tage Plads og lod
halvt uforstaaende. Men med et Hovedkast bad den vrede Mand sig
fritaget for Talemaader og fordrede klar Besked. Et Ja eller et Nej
vilde han have — intet andet.
Per tøvede endnu med Svaret. Han tænkte paa, at dersom han nu
sagde nej, vilde han rimeligvis aldrig se Fransisca mere — og hans
Hjerte blev tungt derved. Han saae hende for sig, saadan som hun
rimeligvis nu gik derinde i Huset ved Siden af og i Angst og
Spænding ventede paa Udfaldet. Og virkelig foer i dette Øjeblik den
Tanke ham som en Lysning gennem Sjælen at lade alle tvivlsomme
Storheds-Drømme fare, at holde fast paa denne ene, sikre lille
Lykke-Spurv, han holdt i sin Haand, og glemme Guldfuglene højt paa
Tage og Tinder. Men atter dukkede Neergaards nøgne Hoved op for
ham. Og han rettede sig i Vejret og svarede aabent Nej.
Per var bleven kridhvid af Raseri; men han rørte sig ikke, svarede
ikke heller. Dog var det ikke Mandens Trusler, der gjorde ham tavs.
Han havde før været ude for et Par knyttede Næver, og hans første
Tanke, da han saae Manden nærme sig, havde da ogsaa været den
at gribe ham i Kværken og sætte ham op imod Væggen og holde
ham fast der, indtil han havde faaet Galskaben sprællet af sig. Men
da han stirrede ned i dette blege, fortrukne Ansigt med den dirrende
Mund, der endnu tydeligere end hans fremstammede Tale viste, hvor
nær den hele Sag var gaaet ham, hvor alvorligt den havde græmmet
og ydmyget ham, rørte der sig i Dybet af hans Sind en Følelse af
Skyld, der holdt hans Haand nede og hans Mund lukket.
Det var altsaa igen hans „Samvittighed“, der havde overlistet ham,
— dette ubestemmelige, spøgelseagtige noget, der pludselig satte
En et Troldspejl for Øjnene, hvori man saae sig selv i hæslig
forvrænget Skikkelse. Han, som havde troet sig saa lykkelig frigjort
for alle Slags Pukler og Knyster paa Sjælen, stod atter her
beskæmmet som en Nar, — og Ærgrelsen herover var nær ved at
faa ham til at glemme baade Fransisca og Adskillelsen fra hende.
Men atter var Lykken ham huld. Ikke mange Dage efter skete der
noget, der ikke alene fik ham til at glemme den bratte Udstødelse af
Kærlighedens Paradis, men næsten tog sig ud som selve
Skæbnegudens opmuntrende Vink til ham — en Belønning for hans
Standhaftighed. Havde han længe ligget og kæmpet i Dødvande og
biet paa en gunstig Vind for sin Eventyrsejlads gennem Livet, saa
rejste der sig i det hele nu en Storm af Begivenheder omkring ham,
der bar ham ud paa det aabne Hav.
***
Allerede for noget siden var han naaet saa vidt med Arbejdet paa
sit Projekt, at han havde ment igen at turde forelægge en Avtoritet
det til Bedømmelse. Han havde dennegang henvendt sig til
Formanden for Ingeniørforeningen, en pensioneret Ingeniøroberst,