Modules

Unit 07

Python modules are used to store a collection of functions and variables that can be imported into other modules, scripts, notebooks, etc.

A module is a file containing Python definitions and statements. The file name is the module name with the suffix .py appended.

Consider the following module mytimemodule.py:

"""My own module about important time questions"""

import datetime

HOURS_PER_DAY = 24

def get_current_datetime():
    """
    Get current time and day
    """
    now = datetime.datetime.now()
    return now


def days2hours(days):
    """
    Convert days to hours

    This function takes a number of days as input 
    and calculates the equivalent number of hours.
    """
    hours = days * HOURS_PER_DAY
    return hours

If you put the file in the working directory of your notebook or current python session (important!), you can import it and use it like other modules:

import mytimemodule
mytimemodule.get_current_datetime()
datetime.datetime(2025, 1, 31, 9, 30, 1, 608212)

Also, the global variable HOURS_PER_DAY

print(f"There are {mytimemodule.HOURS_PER_DAY} hours per day!")
There are 24 hours per day!

That’s how easy you can reuse code in another script. Soon, you will learn how to avoid the inconvenience of putting the module in your current working directory, by putting your module in a package and making the package available in your conda environment.

Keeping your module organized

Pay attention to how I organized mytimemodule:

  1. Module-level docstring: The documentation of your module comes first
  2. Imports follow right after
  3. Module-level variables are usually defined before
  4. Functions

The convention is to write module-level variables that are constants in UPPERCASE letters, variables that may be altered in lowercase.

reload: a useful trick

You regularly use the import statement at this point. Now that you also start coding your own modules you will likely find this trick useful:

Imagine you create a copy of mytimemodule.py yourself and import it to a notebook to explore its functions. You have a great idea for adding a new function. After implementing your new function in the module, you want to test it in your notebook, but you get an error.

The python interpreter of your session still uses the state of the module at the time of the initial import. So it thinks the new function does not exist. Running the import again does not work. You could either restart the kernel and run all cells of your notebook again, or you can use a special function to reload it:

from importlib import reload
reload(mytimemodule)

Learning checklist

  • I know that modules are a great place to store functions and variables that I will need to reuse in future projects, notebooks, and scripts.
  • I know how to import module files (and I am aware that the file needs to be located in the current working directory for the import to work).
  • I can apply functions that are given to me in modules.
  • I know how to reload modules when I am actively changing code in the module file that I want to use right away in a notebook or script.

Interactive exercise: our own module

#06-01: Our module solar

In this exercise you will create our own python module solar that contains a number of useful functions related to analyzing solar radiation data, the engine behind photovoltaic energy production.

  • Below, you will find a concise summary of all the math equations you will need for your module. In case you are interested to understand the theory behind these equations on a deeper level, you will find many resources on the internet.
  • Use this file solar.py as a starting point to the module you will write for this exercise.
  • Closely look at the functions convert_utc2localsolar() and comp_hourangle() and try to understand how they handle time, as well as how they are implemented in general.
  • Add several functions to the module: comp_declination(), comp_elevangle(), comp_solarazimuth(), comp_cos_incidenceangle(), comp_irradiance_incline(). Use the functions that I already provided in the module as template.

Radiation onto a tilted surface

The declination \(\delta\) is the angular measure of the sun’s position relative to our equator, changing throughout the year as Earth orbits the sun. It is a function of the day of year \(n\): \[ \delta = \epsilon \cdot sin(2\pi \cdot \frac{n-80}{365})\] where \(\epsilon = 23.45 ^\circ\) is the obliquity of the ecliptic.

The hour angle \(\tau\) is the measure of time since solar noon, expressed in angular units, indicating the position of the sun in the sky relative to the observer’s meridian. At noon true local time, \(\tau = 0\), it is negative in the morning and postive in the afternoon. To compute the hour angle (in degrees) from an actual time, we first calculate local solar time (LST), here starting off of UTC: \[t_{LST} = t_{UTC} + \frac{\lambda}{15}\] where \(t_{UTC}\) and \(t_{LST}\) are the wall times in UTC and local solar time, respectively. \(\lambda\) is the geographical longitude. In Dornbirn, \(\lambda = 9.7438 ^\circ\).

Then, local solar time can be converted to angular values by \[\tau = 15 \cdot (t_{LST} - 12)\] To make the resulting sign of \(\tau\) fall into the convention mentioned above, all values of \(\tau > 180\) need to be subtracted with \(360\).

The elevation angle \(h\) of the sun represents its apparent height above the observer’s horizon, with \(90 ^\circ\) denoting directly overhead and \(0 ^\circ\) at the horizon. We computed it during Workshop 1, and use the same formula in a slightly rearranged format: \[h = arcsin(cos(\delta) \cdot cos(\tau) \cdot cos(\varphi) + sin(\delta) \cdot sin(\varphi))\] where \(\varphi\) is the geographical latitude, again in Dornbirn \(\varphi = 47.4124 ^\circ\).

The solar azimuth \(\alpha\) represents the horizontal direction of the sun from an observer’s location. While in meteorology and navigation the azimuth is usually measured clockwise from north, in astronomy the solar azimuth \(\alpha = 0\) when the sun is in the south. \[ \alpha = arccos(\frac{sin(h)\cdot sin(\varphi) - sin(\delta)}{cos(h)\cdot cos(\varphi)})\]

Due to the \(arccos\) function, the computation of morning/afternoon angles is ambiguous. We can eliminate the problem by multiplying \(\alpha\) with the sign of \(\tau\) (remember that \(\tau\) was negative in the morning, and positive in the afternoon). This, however, changes the angle representation to negative values in the East, and postive ones in the West. To bring our angle format back to the convention (\(0\) in the South increasing clockwise to \(360 ^\circ\)), we add \(360 ^\circ\) and then take the modulo to ensure that no values are \(> 360 ^\circ\). To sum up, we modify \(\alpha\) from the formula above by \[\alpha = (\alpha \cdot \text{signum}(\tau) + 360) \bmod 360\]

The incidence angle \(\theta\) describes the angle between the direction of the direct sun light and the normal (perpendicular) to the surface of interest (i.e., the actual horizontal surface, or in our case a tilted PV module). This angle impacts the efficiency of energy absorption. \[cos(\theta) = cos(\beta_{PV}) \cdot sin(h) + sin(\beta_{PV}) \cdot cos(h) \cdot cos(\alpha - \alpha_{PV})\] where \(\beta_{PV}\) is the incline angle of the PV module and \(\alpha_{PV}\) is the azimuth of the PV module. For today’s implementation we are only interested in PV modules that are inclined towards the South, so \(\alpha_{PV} = 0\).

Again, we need to consider a special case: Whenever \(cos(\theta) < 0\) the sunlight does actually not shine onto the surface of our PV module. We can therefore set all these values to \(0\).

Finally, with all the information above, we can compute the solar irradiance \(G\) onto an inclined surface, such as a PV module, for any location on Earth at any time of year and day by \[G = \frac{G_0}{sin(h)} cos(\theta)\] where \(G_0\) is the solar irradiance onto a horizontal surface. We typically obtain \(G_0\) from measurement or model simulations. In our solar module we will parameterize \(G_0\) and use it as an upper-limit proxy for clear-sky solar irradiance. The parameterization is based on the solar constant \(G_{SC}\) and considerations around the location on Earth as well as the day of year \(n_{doy}\). It is already implemented in the starter kit module you downloaded above and reads

\[G_0 = G_{SC} \cdot \left(1 + 0.033 \cdot \cos\left(\frac{2 \pi \cdot n_{doy}}{365}\right)\right) \cdot \cos(\varphi) \cdot \cos(\delta) \cdot \sin(h)\]

Disclaimer

The above equations are based on simplifications and approximations. Use the information without warranty.