OpenFOAM cases are setup through a hierarchy of text files. Git is made to track changes in code, which is text. This makes Git a perfect candidate for tracking changes in simulation settings and parameters. In this post I will not really give a full picture of Git's operation and capabilities, but detail how I use Git with OpenFOAM cases.

Why track changes?

Setting up an OpenFOAM case can be a real headache, since there are so many settings and parameters to change. In my experience, it's nice to be able to keep track of settings that worked, in case I change something and the simulation starts crashing. With Git it's easy to "check out" the last configuration that worked, and also keep track of what changes had favorable or negative effects, using commit messages.

Branches

Git has the ability to create branches, that is, versions of files that one may switch between and edit independently. This is useful for OpenFOAM cases, because sometimes it's desirable to run multiple cases with some parameter variation. For example, one might want to run one simulation with a low Reynolds number mesh and one with a high Reynolds number mesh. One may also want a case that can be run with different versions of OpenFOAM. By putting these files on different branches, one can switch between these two sets of parameters with a single checkout command.

Sharing, collaboration, and storage

Git has become the most popular way to share and collaborate on code, through the website GitHub. Using GitHub, another user may "fork" a set of case files, make some changes, and submit a "pull request" back to the main repository. This can be much more convenient than simply sharing zipped up folders of entire cases, as changes are more visible and reversible. Putting case files in a remote repository (private repositories can be hosted for free on BitBucket) is also a nice way to keep things backed up.

How to set up a Git repository inside a case

The first important step is to create a .gitignore file in the top level case directory. Inside this file, you tell Git the file names or name patterns to not track. For OpenFOAM simulations, it is probably undesirable to track the actual simulation results, since this will grow the size of the repository significantly every time the simulation results are committed, i.e., all committed results would be saved for all time. My sample .gitignore file is shown below:

*.gz
log.*
oldLogs/
postProcessing/
images/
video/
*~
processor*/
0*/
1*/
2*/
3*/
4*/
5*/
6*/
7*/
8*/
9*/
!0.org
constant/extendedFeatureEdgeMesh
*.eMesh
constant/polyMesh/*
!constant/polyMesh/blockMeshDict
constant/cellToRegion
constant/dynamicMeshDict
*.OpenFOAM

Note that I also ignore application logs, though these may be valuable troubleshooting tools. For example, one could checkout a previous commit, look at a log and compare with a newer version. However, these logs can get big, so I leave general performance information to the commit messages.

After a proper .gitignore file has been created, a repository can be initialized by running git init in a terminal (inside the top level case directory). Running git status will then list all the files that are not yet tracked. To add all of these files and create the first commit, run

git add -A
git commit -m "Initial commit message goes here"

Next, you may want to create a remote repository and set the remote location for the local repository. If using GitHub, create a new empty repository and follow these instructions for pointing your repository there.

Next, "push" your local changes to the remote by running

git push origin master

This pushes the changes to the master branch of the remote repo.

The case is now set to be tracked with Git. You can now continue on, and save your path towards, the perfect OpenFOAM case!

Traversing carriage assembly installed as part of the UNH-CORE turbine test bed.
Traversing carriage assembly installed as part of the UNH CORE turbine test bed.

In order to perform turbine wake measurements in UNH's tow tank, I designed and built a 2-axis positioning system for our Nortek Vectrino acoustic Doppler velocimeter. Some specifications are shown in the table below:

Cross-stream (y-axis) travel 3.0 m
Vertical (z-axis) travel 1.2 m
Max drag force (estimated) 25 N at 2 m/s

Components

SolidWorks rendering of the YZ traverse assembly.
SolidWorks rendering of the YZ traverse assembly.

The frame is constructed mostly from 80/20 15 series t-slot framing and hardware, with some custom brackets to mount to 1.25" pillow block linear bearings.

Attached to the frame are two Velmex BiSlide linear stages, with the y-axis driven by a stepper/belt and the z-axis driven by a stepper/ball screw. An additional igus DryLin polymer linear bearing, along with a second carriage on the z-stage BiSlide provide additional moment loading capacity.

The Vectrino probe is clamped in a cantilevered bar attached to a NACA 0020 strut. The foil is attached to an 80/20 extrusion, which mounts to the z-axis linear stage via two custom adapter blocks. Note that the adapter blocks' odd odd trapezoidal shape is due to the fact they're made from recycled material.

Two igus Energy Chain cable carriers span the horizontal and vertical axes to keep the Vectrino and motor cables from tangling during operation.

Actuation and control

The stepper motors are driven by an ACS UDMlc drive controlled by an ACS NTM EtherCAT master controller, which provides synchronized operation with the tow and turbine axes through ACS' SPiiPlus interface. In addition to the ability to write motion control programs with their ACSPL+ language, COM, and C library, the ACSpy Python wrapper for the C library allows incorporating motion commands into Python programs, e.g., TurbineDAQ.

CAD file download

A full assembly of this design, minus some minor hardware, is available for download in (SolidWorks 2012 format) here. Contact me if you would like something similar designed and/or built.

Having used SolidWorks for a while, and likely entering the job market again in O(1) years, I was curious as to which CAD software would be wisest to learn next, with respect to job opportunities and salaries. Looking to compile data from the current job market, I wrote Indeedy, a Python module for automatically searching Indeed.com. With this new tool I searched for "mechanical engineer" plus the names of various CAD systems, then plotted the number of results and average salary for each on the bar graph shown below.

Search results for "mechanical engineer" plus various CAD systems on Indeed.com.
Search results for "mechanical engineer" plus various CAD systems on Indeed.com.

From these results we can see demand for SolidWorks is dominant by a large margin. However, Catia and NX skills are compensated better on average, which makes sense considering their popularity in the aerospace and automotive industries. At first glance it seems there are more Catia than NX jobs, but when NX's results are combined with Unigraphics' (NX's ancestor), demand is comparable. However, this combination also comes with with a decrease in average salary, giving Catia a slight edge, and the title of Best CAD System to Learn (for me anyway).

A photo of the completed stand with bike.
Bike rack stand assembly.

Living in a studio apartment, storage space can be hard to come by. To have a place to store both my bike and hitch receiver bike rack, I designed and built a rolling stand that holds the rack upright, also serving as a nice work stand.

The frame is made from 80/20 t-slot extrusions and hardware, which are available at Amazon.com. On top is a 2 inch Reese hitch receiver, and below is a set of swivel casters, two of which are locking. One nice feature of this design is that it only requires cutting and tapping—no welding, drilling, or more complicated machining necessary before it's ready for assembly.

The SolidWorks 2012 CAD models for this assembly can be downloaded here.

Open the IPython Notebook server inside a directory from the Windows context menu.
Open the IPython Notebook server inside a directory from the Windows context menu.

By default, the awesomely useful and fun IPython Notebook does not integrate with Windows so seamlessly. A console and then the notebook server must be opened in the proper directory in order to open a new or existing notebook. These extra steps make the IPython Notebook slightly less ideal for quickly jotting down ideas—one of its greatest uses!

Not to fear, however, as the Windows Registry can easily be modified to

  1. Open notebooks directly from Windows Explorer.
  2. Create a shortcut to launch the IPython Notebook server within the current directory.

Simply download and run ipython.reg to add the entries shown below to your registry automatically.

These entries were put together from instructions located here. A similar solution can also be found here.

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\Directory\Background\shell\ipynb]
@="Open IPython Notebook server here"

[HKEY_CLASSES_ROOT\Directory\Background\shell\ipynb\command]
@="\"C:\\Python27\\Scripts\\ipython.exe\" \"notebook\" \"%V\""

[HKEY_CLASSES_ROOT\Directory\shell\ipynb]
@="Open IPython Notebook server here"

[HKEY_CLASSES_ROOT\Directory\shell\ipynb\command]
@="\"C:\\Python27\\Scripts\\ipython.exe\" \"notebook\" \"%V\""

[HKEY_CLASSES_ROOT\ipynb_auto_file\shell\open\command]
@="\"C:\\Python27\\Scripts\\ipython.exe\" \"notebook\" \"%1\""

The code below shows how to use PyQt to create a status bar that shows the progress of an OpenFOAM simulation. Note that the arguments to re.findall may need to be tweaked depending on the specific case setup, and that the code assumes the simulation will be run in parallel, but it should be easy enough to tailor this sample to any case.

Qt Progress Bar

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
This program creates a progress bar for an OpenFOAM simulation
Based on http://acaciaecho.wordpress.com/2011/01/11/pyqtprogressbar/

Put this file in your case folder and run from a terminal
"""
import re
import os
import time
import numpy as np
from PyQt4 import QtCore, QtGui


def getParams():
    """Get run parameters"""
    f = open("system/controlDict", "r")
    for line in f.readlines():
        if "endTime" in line:
            endTime = re.findall("\d.\d+", line)
            if endTime == []:
                endTime = re.findall("\d+", line)
        if "writeInterval" in line:
            writeInterval = re.findall("\d.\d+", line)
    f.close()
    endTime = endTime[0]
    writeInterval = writeInterval[0]
    return endTime, writeInterval



class Progress(QtCore.QThread):
    procDone = QtCore.pyqtSignal(bool)
    partDone = QtCore.pyqtSignal(int)
    
    def run(self):
        endTime, writeInterval = getParams()
        done = False
        
        while not done:
            # Find highest valued folder in "processor0" folder
            if os.path.isdir(endTime):
                done = True
                self.partDone.emit(100)
            else:
                dirs = os.listdir("processor0")
                numdirs = np.array([])
                for d in dirs:
                    try:
                        numdirs = np.append(numdirs, float(d))
                    except ValueError:
                        pass
                self.partDone.emit(int(np.max(numdirs)/float(endTime)*100))
            time.sleep(1)
        self.procDone.emit(True)   


class AddProgresWin(QtGui.QWidget):
    def __init__(self, parent=None):
        super(AddProgresWin, self).__init__(parent)

        self.thread = Progress()
        self.nameLine = QtGui.QLineEdit()
    
        self.progressbar = QtGui.QProgressBar()
        self.progressbar.setMinimum(1)
        self.progressbar.setMaximum(100)
        self.progressbar.setFixedWidth(400)
    
        mainLayout = QtGui.QGridLayout()
        mainLayout.addWidget(self.progressbar, 0, 0)
    
        self.setLayout(mainLayout)
        self.setWindowTitle("Solving")

        self.thread.partDone.connect(self.updatePBar)
        self.thread.procDone.connect(self.fin)
        self.thread.start()

    def updatePBar(self, val):
        self.progressbar.setValue(val)
    
    def fin(self):
        sys.exit()
        

if __name__ == '__main__':
    import sys
    app = QtGui.QApplication(sys.path)

    pbarwin = AddProgresWin()
    pbarwin.show()

    app.exec_()

OpenFOAM runs can take a long time. Wouldn't it be nice to know when a simulation is done without having to keep checking the terminal? As it turns out, this is very easy to set up with Python (I got most of the code I used from here, which details how to send an SMS). Simply create a script called send_email.py in the OpenFOAM case directory that looks like this:

#!/usr/bin/python

import smtplib
from email.mime.text import MIMEText

server = smtplib.SMTP("smtp.gmail.com", 587)
server.starttls()
server.login("<username>", "<password>") 

msg = MIMEText("The simulation is complete.")
msg["Subject"] = "Simulation finished"
msg["From"] = "Me"
msg["To"] = "Me"

server.sendmail("Me", "my_email_address@gmail.com", msg.as_string())

Replace all the relevant info with your own. Note that this assumes you're using Gmail, but it can be adapted to any SMTP server. The script could also be expanded to email team members, extract information about the run, etc.

Change the permissions such that the file can be executed as a program, then at the bottom of your Allrun script add

./send_email.py

Voila. Now you'll get an email when your simulation finishes, and can go off and be productive elsewhere in the meantime.

The BeagleBone Black (BBB) is a $45 credit-card-sized computer that runs embedded Linux. I recently purchased a BBB along with an $8 4-phase stepper motor and driver to start playing around with slightly "closer-to-the-metal" motion control.

A shot of the BeagleBone Black, stepper motor, and driver.
BeagleBone Black wired up with the stepper motor and driver.

My first objective with the BBB was to simply get the stepper to move, and for this I started using the included Cloud9 IDE and BoneScript library. After a while messing around and learning a little JavaScript, along with some help from my programmer brother Tom, the stepper was moving in one direction using wave drive logic. However, the code felt awkward. I decided I'd rather use Python, since I'm a lot more comfortable with it, so I went out in search of Python packages or modules for working with the BeagleBone's general purpose I/O (GPIO) pins. I found two: Alexander Hiam's PyBBIO and Adafruit's Adafruit_BBIO. Adafruit_BBIO seemed to be more frequently maintained and better documented, so I installed it on the BeagleBone per their instructions, which was pretty quick and painless.

Next, I wrote a small, simple python module, BBpystepper, with a Stepper class for creating and working with a stepper control object. Currently the control uses full-step drive logic, with wave drive available by changing Stepper.drivemode. I may add half-stepping in the future, but for now full-stepping gets the job done.

Usage

After installing Adafruit_BBIO and BBpystepper on the BeagleBone, the module can be imported into a Python script or run from a Python interpreter. For example:

>>> from bbpystepper import Stepper
>>> mystepper = Stepper()
>>> mystepper.rotate(180, 10) # Rotates motor 180 degrees at 10 RPM
>>> mystepper.rotate(-180, 5) # Rotates motor back 180 degrees at 5 RPM
>>> mystepper.angle
0.0

Notes

  • By default the GPIO pins used are P8_13, P8_14, P8_15, and P8_16. These can be changed by modifying the Stepper.pins list.
  • By default the Stepper.steps_per_rev parameter is set to 2048 to match my motor (it has a built-in gearbox).
  • The code doesn't keep track of where it ends in the sequence of pins. It simply sets all pins low after a move. This means there could be some additional error in the Stepper.angle variable if the amount of steps moved is not divisible by 4.

Final Thoughts

As mentioned previously, half-stepping would be a nice future add-on, along with a more accurate way of keeping track of the motor shaft angle. Another logical next step would be to use one of the BBB's Programmable Real-Time Units (PRUs) to control the timing more precisely, therefore improving speed accuracy and allowing synchronization with other processes, e.g. data acquisition. However, for now this simple method gets the job done.

Having settled in on the Cummins B3.3 for an engine, the next step was to choose a vehicle to put it in. I had some rough requirements in mind:

  • Seating capacity: At least 4 people
  • Air conditioning and decent acoustics/sound system
  • 4-wheel drive

Since the original inspiration for the project was a Cummins B3.3 in a Jeep Wrangler (YJ), that was an obvious candidate, but I also had an affinity for and experience with Ford body styles and interiors. The decision came down to two potential chassis: Wrangler vs. Explorer. Both satisfied the basic requirement above, but there were some more criteria to consider---fitment, noise and comfort, emissions, initial cost, and fuel economy.

Fitment

An obvious requirement was that the engine fit inside the vehicle's engine bay. This contest goes to the Wrangler. The engine bay is huge, and the hood opens very wide. With the Explorer, a body lift would most likely be necessary.

Winner: Wrangler

Noise/acoustics and overall comfort

The fun and excitement of the Jeep's removable top comes along with a noisy ride, and poor acoustics. For me, the Explorer's acoustics and comfort were worth the somewhat less hip styling. Electric windows and locks were icing on the cake.

Winner: Explorer

Initial cost

On the used market, Wranglers fetch about 2–3 times the amount Explorers do. Spending less on a chassis would save more for the engine, giving the Explorer the edge.

Winner: Explorer

Emissions

Since this vehicle would likely throw many error codes with an OBD-II computer, it was important to find a vehicle that was OBD-I. The TJ body style is not available in the OBD-I years, while the 2nd generation Explorer body style was just introduced in 1995, barely making the cutoff. The Explorer body style and interior both beat the YJ, as discussed above, so the Explorer gets the nod here.

Winner: Explorer

Fuel economy

The most important metric—and the main goal of the project—was to achieve high fuel efficiency. Thus, this metric would likely decide the contest. Properties for the two vehicles are tabulated below. On one hand, the Wrangler is lighter than the Explorer, requiring less energy to get up to speed. However, the Wrangler is also less aerodynamic, consuming more energy while cruising. To compare the two, a typical driving scheme was devised, for which rough estimates for fuel consumption were calculated. This driving scheme included

  • Accelerating 0–40 mph in 10 seconds
  • Cruising at 40 mph for 5 miles
Explorer Wrangler
Mass (kg) 1806 [1] 1331 [2]
Drag coefficient 0.43 [3] 0.55 [3]
Frontal area (\text{m}^2) 2.3 2.3

Calculating fuel consumption during cruising

This was the easy part. Neglecting friction in the tires and drivetrain—which would be more or less equivalent between both vehicles—energy consumption during cruising can be estimated by integrating the drag force over the distance traveled.

Drag force is estimated using the formula

F_D = \frac{1}{2}\rho A_f C_D V^2,


where \rho is the density of air, A_f is the vehicle's frontal area (approximately equal for both Explorer and Wrangler), C_D is the vehicle's drag coefficient, and V is the cruising speed. Since speed is constant, the integration simplifies to multiplication, giving (in Nm)

E_{\mathrm{cruise}} = 3,550,673 C_D.

Calculating fuel consumption during acceleration

Calculating the energy required during acceleration included consideration of both drag and inertial forces. Since drag is not constant during acceleration, we will use a more general method and integrate the varying power over the acceleration time, which is simply the force multiplied by velocity, or

E_{\mathrm{acc}} = \int_0^{10} \left( F_D V + maV \right) \, \mathrm{d}t.


Integrating the constant acceleration gives the vehicle speed as a function of time, i.e.,

V(t) = \frac{\mathrm{d}x}{\mathrm{d}t} = 1.79 t.


Boiling this all down and substituting all but the variables that differ between the vehicles, we obtain

E_{\mathrm{acc}} = 19,787 C_D + 160.2m.

The table below summarizes the results for both vehicles. As you can see, these estimates show the Explorer consuming 16% less energy than the Wrangler, making it a better choice with regards to fuel economy.

Winner: Explorer

Explorer Wrangler
Energy consumed during cruising (Nm) 1.53 \times 10^6 1.95 \times 10^6
Energy consumed during acceleration (Nm) 2.98 \times 10^5 2.24 \times 10^5
Total energy consumed (Nm) 1.83 \times 10^6 2.17 \times 10^6

Conclusions

Obviously, since this post is in a series about a diesel Explorer, you can guess which chassis won out in the end. The Wrangler is a cool design for sure, but the Explorer's acoustics, comfort, and most importantly aerodynamics made it my choice.

References

[1] http://www.edmunds.com/ford/explorer/1995/features-specs.html

[2] http://www.edmunds.com/jeep/wrangler/1995/features-specs.html

[3] http://ecomodder.com/wiki/index.php/Vehicle_Coefficient_of_Drag_List