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.
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]:
If you installation was successful, you should see a version Number like '1.3.4'
.
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)
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__}")
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]:
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.
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.
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
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]:
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]:
The plot with no tools is shown below.
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 [ ]: