Bokeh is a plotting library for Python like Matplotlib and Alatir (https://altair-viz.github.io/). While Matplotlib is widely used and great for making static 2D and 3D plots, building interactive web graphics is not its strong suite. Bokeh on the other hand is a plotting library designed for the web. You can create static plots with Bokeh, but its real strength is that Bokeh can make online interactive plots (without knowing any Javascript).

In this post, we will build a Bokeh plot that plots a Mohr's Circle. Mohr's circle is a useful way for Engineers to visualize the normal and shearing stresses of an element that is rotated relative to the known applied stress. Engineers use Mohr's Circle to help determine how much load a component can withstand before it starts to deform.

Installing Bokeh

Bokeh comes installed in the full Anaconda Distribution of Python. If you are using the (base) Anaconda environment, no other installation steps are necessary.

If you don't have Anaconda installed or are using a virtual environment, Bokeh can be installed using conda and the Anaconda Prompt using the command:

> conda install bokeh

Or installed at a terminal using pip.

$ pip install bokeh

Once bokeh is installed you can bring up the Python REPL (the Python prompt) and confirm your installation with the following code:


In [1]:
>>> import bokeh
>>> bokeh.__version__


Out[1]:
'1.3.4'

If you installation was successful, you should see a version Number like '1.3.4'.

Create a Mohr's Circle Function

Before we start plotting with Bokeh, we'll first make a function for Mohr's Circle. The function below can from an amazing student in on of my courses. The student took on the challenge of building Mohr's Circles with Python and the function below was part of the solution.


In [2]:
import numpy as np

def mohrs_circle(stress_x=1,stress_y=1,shear=0):
    """
    A function that calculates the critical values to build a Mohr's Circle
    """
    
    # calculate the average stress, min stress and max stress
    stress_avg=(stress_x+stress_y)/2
    stress_max=stress_avg+(((stress_x-stress_y)/2)**2+shear**2)**0.5
    stress_min=stress_avg-(((stress_x-stress_y)/2)**2+shear**2)**0.5
    # calculate the radius
    R=((((stress_x-stress_y)/2)**2)+shear**2)**0.5     #Also max shear
    circle_eqn=((stress_x-stress_avg)**2)-shear**2-R**2
    
    # Construct x and y arrays that build the circle
    n=100
    t=np.linspace(0,2*np.pi,n+1)
    x=R*np.cos(t)+stress_avg
    y=R*np.sin(t)
    
    # Construct X and Y arrays that build the line accross Mohr's circle
    X = np.array([stress_x, stress_y])
    Y = np.array([-shear, shear])
    
    # Declare the center
    C = stress_avg

    return x,y,X,Y,R,C

Let's test our function and see the resulting output


In [3]:
x,y,X,Y,R,C = mohrs_circle(2,5,1)
print(X)
print(Y)
print(C)
print(R)


[2 5]
[-1  1]
3.5
1.8027756377319946

We see output that looks reasonable.

Build Mohr's Circle with Bokeh

Next we'll use our mohrs_circle() function to build a plot of Mohr's Circle using Bokeh. The imports start our script.


In [4]:
import bokeh
from bokeh.plotting import figure, output_file, show, output_notebook
from bokeh.models import ColumnDataSource

print(f"Bokeh version: {bokeh.__version__}")


Bokeh version: 1.3.4

Next, we'll call our mohrs_circle() function so that we have arrays we need to build the plot.


In [5]:
x,y,X,Y,R,C = mohrs_circle(2,5,1)

Now we'll use the arrays x,y and X,Y to create two Bokeh Columnar Data Sources. Bokeh uses the concept of a columnar data source, sort of like a column in a table or excel file to build plots.


In [6]:
# Create the Bokeh Column Data Source Object from the mohrs_circle() output arrays
circle_source = ColumnDataSource(data=dict(x=x, y=y))
line_source = ColumnDataSource(data=dict(x=X, y=Y))

The next step is to create a Bokeh figure object that we'll call plot. Bokeh figure objects are the basis for Bokeh plots. Lines, input boxes, sliders and other sorts of things can be added to a figure object. Whatever gets added to the figure object will be shown in the final plot.

Keyword arguments such as plot_height, plot_width, title and tools can be called out when the figure object is created.


In [7]:
plot = figure(plot_height=400, plot_width=400, title="Mohr's Circle", tools="pan,reset,save,wheel_zoom")

Now we can add our circle and line to the plot. This is accomplished by calling plot.line() and specifying the axis 'x','y' and providing our column data sources as keyword arguments. Some line attributes such as line_width and line_alpha can also be specified.


In [8]:
plot.line('x','y', source=circle_source, line_width=3, line_alpha=0.6)
plot.line('x','y', source=line_source, line_width=3, line_alpha=0.8)


Out[8]:
GlyphRenderer(
id = '1041', …)

OK, we've created our plot, but now we need to see it. There are a couple ways of doing so. Remember that Bokeh is primarily designed for creating plots built for the web.

Show the plot in a separate window

The first way we can see the plot is by using the show() function. Pass in the plot as an argument to the show() function. If you are building the Bokeh Plot in a Jupyter notebook, this will pop out a new browser tab and you'll see your plot.


In [9]:
show(plot)

The plot should look something like the plot below.

You can click the little [save] icon to save the plot.

Show the plot in a Jupyter notebook

If you are working in a Jupyter notebook and want to see the plot inline, call the output_notebook() function at the top of the notebook and then show the plot with show(). Note that you can't show a plot in a separate window and show the a plot inline in the same Jupyter notebook.

The code below is the exact same as the code we used to build the plot above, the only difference is the line

output_notebook()

right below the imports.

import bokeh
from bokeh.plotting import figure, output_file, show, output_notebook
from bokeh.models import ColumnDataSource

output_notebook()

x,y,X,Y,R,C = mohrs_circle(2,5,1)
circle_source = ColumnDataSource(data=dict(x=x, y=y))
line_source = ColumnDataSource(data=dict(x=X, y=Y))

plot = figure(plot_height=400, plot_width=400, title="Mohr's Circle", tools="pan,reset,save,wheel_zoom")

plot.line('x','y', source=circle_source, line_width=3, line_alpha=0.6)
plot.line('x','y', source=line_source, line_width=3, line_alpha=0.8)

show(plot)

You will see the plot in the resulting output cell look something like below

Output the plot as a .png file

If you want to output the plot as a .png file, you first have to make sure that a couple of libraries are installed. This is most easily completed at the Anaconda Prompt using conda.

> conda install selenium pillow
> conda install -c conda-forge phantomjs

Then you can use Bokeh's export_png() function to save the plot to a .png file. Make sure to import export_png before calling the function


In [10]:
from bokeh.io import export_png

export_png(plot, filename="plot.png")


Out[10]:
'C:\\Users\\Peter\\Documents\\staticsite\\content\\code\\bokeh\\plot.png'

On my Windows 10 laptop, a Windows Defender Firewall window popped up that asked me for access. After I typed in my administrator password, the plot was saved.

It should look like the plot below

If you don't like the pan, zoom, refresh, and save buttons on the side, they can be removed when you create the plot using Bokeh's figure() function and setting the .toolbar.logo attribute to None and the .toolbar_location to None


In [14]:
import bokeh
from bokeh.plotting import figure, output_file, show, output_notebook
from bokeh.models import ColumnDataSource
from bokeh.io import export_png

x,y,X,Y,R,C = mohrs_circle(2,5,1)
circle_source = ColumnDataSource(data=dict(x=x, y=y))
line_source = ColumnDataSource(data=dict(x=X, y=Y))

plot = figure(plot_height=400, plot_width=400, title="Mohr's Circle", tools="")
plot.toolbar.logo = None
plot.toolbar_location = None

plot.line('x','y', source=circle_source, line_width=3, line_alpha=0.6)
plot.line('x','y', source=line_source, line_width=3, line_alpha=0.8)

export_png(plot, filename="plot_no_tools.png")


Out[14]:
'C:\\Users\\Peter\\Documents\\staticsite\\content\\code\\bokeh\\plot_no_tools.png'

The plot with no tools is shown below.

Conclusion

In this post, we build a plot of Mohr's Circle using Bokeh. Bokeh is a Python package that can be installed with conda or pip. We built the plot in a couple of steps. First we imported some function from bokeh. Next, we wrote a Python function that gave us the arrays necessary to plot Mohr's Circle. Using the arrays we created, we defined Bokeh Column Data sources for the circle and the line. Next we created a bokeh figure and added lines (the circle and line) to it. At the end of the post we showed the figure in three different ways: in a seperate window, in a Jupyter notebook and in an exported .png file.


In [ ]: