Python

NumPy Problems

Axis Size Not $2^N$

Problem: A thin film is represented by an array of vectors ‚a‘ with dimensions (2000 x 500 x 1), but it is not processed properly because it should really be (2048 x 512 x 1) (2^11 = 2048; 2^9 = 512). Solution: aNew = np.pad(a, [(24, 24), (6, 6)]) will surround the data with zeros to make it up to the correct shape

Discretisedfield gives an error to do with iterative unpacking and something not being a multiple of 4

Most likely due to empty .ovf files. Check for any files (probably at the end of the simulation) that do not contain any binary data.

Matplotlib with X11 Forwarding

Need to, at the top of the file, have:

import matplotlib
matplotlib.use('tkagg')

If this still gives an error, may need to replace matplotlib.use('agg') in imported modules (this is the case with discretisedfield)

Finding Where Python Modules Are Stored

python -m site

Getting the Correct Quadrant for Cartesian to Polar Conversions

np.arctan2(y, x)

Similar things exist in other programming languages, e.g. atan2 in C++

Jupyter Notebooks

Mayavi with Jupyter Notebook

At the very start,

from xvfbwrapper import Xvfb
vdisplay = Xvfb(width=1920, height=1080)
vdisplay.start()

Then e.g.

from mayavi import mlab
mlab.init_notebook()
s = mlab.test_plot3d()
s

That last part with just the s is important!

Warning: Cannot change to a different GUI toolkit Error

%matplotlib ... needs to be before importing matplotlib. Every time you change, need to restart kernel

Interactive Matplotlib

%matplotlib notebook

Matplotlib Graphs Missing Axes

Likely caused by importing a module (e.g. discretisedfield) that modifies the plots

Decorators

Usage of Decorators
def decorator(f):
    def new_function():
        print("Extra Functionality")
        f()
    return new_function

@decorator
def initial_function():
    print("Initial Functionality")

initial_function()
@property

Allows accessing of private properties with a getter and setter using object.theProperty.

@classmethod and @staticmethod

Used for functions that are connected to the class itself, and not to instances of it. @classmethod receives class itself as first argument; @staticmethod does not. So a static method is just kind of hanging there because it has a related functionality.

Underscores

More info at https://dbader.org/blog/meaning-of-underscores-in-python#:~:text=A%20double%20underscore%20prefix%20causes,the%20class%20is%20extended%20later

Single leading _var: A convention, signalling that the entity is to be used internally. However, a function defined with a leading underscore will not be imported with from foo import *

Single trailing underscore var_: For defining a variable that is already taken by a keyword, e.g. if you want to pass an argument class to a function, could instead pass class_

Double leading underscore __var: Causes Python to internally rename the attribute to avoid naming conflicts ("name mangling"). Double underscore is often pronounced "dunder". These can be overridden, e.g. by defining a __len__ to redefine how the length of an object is calculated.

Double leading and trailing underscores __var__: Reserved for Python. No "name mangling" here with the two leading underscores.

Difference Between __str__ and __repr__

__str__() is called with print() or str(), and is supposed to be more simplistic. __repr__ is called with repr(), and should provide enough information to construct the object again.

Listing Attributes of an Object

dir(object)

Colour Bar Same Height as Imshow Plot

At top of file, call from mpl_toolkits.axes_grid1 import make_axes_locatable

divider = make_axes_locatable(ax)
cax = divider.append_axes("right", size="5%", pad=0.05)

plt.colorbar(im, cax=cax)

If the above solution doesn't work because x and y axes have different dimensions:

At top of file, call from mpl_toolkits.axes_grid1.inset_locator import inset_axe

axins = inset_axes(ax, width = "5%", height = "100%", loc = 'lower left',
                   bbox_to_anchor = (1.02, 0., 1, 1), bbox_transform = ax.transAxes,
                   borderpad = 0)

cb = fig.colorbar(im, cax = axins)

Axis Ticks Don't Align with Pixels

dx = xTickList[1] - xTickList[0]
dy = yTickList[1] - yTickList[0]

im = ax.imshow(array, extent=(np.min(xTickList) - dx/2, np.max(xTickList + dx/2, np.min(yTickList) - dy/2, np.max(yTickList) + dy/2))

Professional-Looking LaTeX Rendering in Matplotlib

matplotlib.rcParams['text.usetex'] = True at top of file

Unit Testing

Pytest

Generally, run e.g. pytest --exitfirst --verbose --failed-first --cov=. --cov-report html

Unit Testing in GitHub

A useful blog post about this is here.

Add the required modules to requirements.txt using pip freeze > requirements.txt. It is best to do this whilst working in a virtual environment e.g.

python3.8 -m venv .venv
source path/to/.venv/bin/activate

Note that, after creating the virtual environement and installing the required modules including pytest, you need to run

deactivate
source /path/to/.venv/bin/activate

to ensure that the binary of pytest used is that in the virtual environment. May also need to ~pip install pytest-cov for coverage reports.

A sample .yaml file to be placed in .github/workflows is

name: Tests
on: [push]

jobs:
  build:
    name: Run Python Tests
    runs-on: ubuntu-latest

    steps:

    # Chekout the source code
    - uses: actions/checkout@v2

    - name: Set up Python 3.8
      uses: actions/setup-python@v2
      with:
        python-version: 3.8

    - name: Install Python dependencies
      run: |
        python3 -m pip install --upgrade pip
        pip3 install -r requirements.txt        

    - name: Test with pytest
      run: |
        pytest --exitfirst --verbose --failed-first \
        --cov=. --cov-report html