pingswept.org
now with web 1.0 again

July 10, 2009

Drain heat recovery

For the last few months, I've been thinking that a drain heat recovery system might be worth installing in our basement. The idea is that while you take a shower, you run the cold water refilling your water heater through a copper coil wrapped around the hot water going down the drain. There's a nice diagram on the EERE site. (Unfortunately, the EERE site also estimates the payback time at 2.5-7 years, which I think is bunk.) This kind of system doesn't work to recover energy when the draining and refilling doesn't happen at the same time, as with a dishwasher-- when the dishwasher releases the hot water, your water heater has long since refilled (or your on-demand system has turned off).

In April, I measured the ground water temperature in Somerville at 7 C (45 F); I suspect that's close to the annual mean ground water temperature. I also measured a hot shower at 38 C (100 F). This is a little conservative-- I think Sharon usually likes something more like 40 C, but let's say that on average we require water to be heated at least 30 C above the ground water temperature.

The heat capacity of water is 4.2 J/(mL * C), so with a 30 C difference, we're losing around 126 J for each mL poured down the drain. I measured the flow rate of our shower at 2 L in 20 s, which is 100 mL/s. This means we're using 12,600 J/s, or 12.6 kW. A ten minute shower uses 600 * 12,600 J = 7.5 MJ, or 2 kWh.

Drain heat recovery system vendors claim that they can recover almost half of the energy available to the heat exchanger. If we optimistically say we'll always get 50% of the energy back for the next shower, that would be about 1 kWh per shower, or 2 kWh per day, since we each shower every day. Natural gas for our water heater is currently $1.55/therm or $0.053/kWh, so that would save us $0.10/day, or $0.20/day if we assume our water heater has an efficiency of around 50%, which is typical of the crappy gas models like the one in our basement. At the flow rate I mention above, I'd need a heat exchanger about 40 inches long to hit 50% recovery, which would cost around $600 plus installation. If that totals around $1000, the payback time is 5000 days, or at least 13 years, even with the optimistic assumptions I've made above. Unfortunately, even if the system lasted 13 years without corroding, the chances of us living in the same house until then is small enough that I think I'll hold on to my $1000, or put the same money toward a more efficient water heater.

On the other hand, it does suggest that a doubling in the cost of natural gas plus self-installation would make it a win. If you own a drain that handles more than ~4 showers per day in the Northeast (like in gyms or apartment buildings), you'd have to be an idiot not to install one.

June 28, 2009

Anyone around Boston have some free/broken hedge trimmers?

Greetings, fellow internet denizen! I am concocting a device that I call The Electric Lawn Mower That Doesn't Suck.

I have an electric lawn mower that is large, heavy, almost as loud as a gas mower, and only works with a cord attached. I also have a reel mower that is pretty sweet, but it can't cut grass taller than approximately the radius of the blade cylinder, so it leaves stragglers.

I think if I could mount a hedge trimmer on something like a reel mower and power it off a cordless drill battery, I'd be golden. Hedge trimmers are pretty loud, but I think I could rework the drive mechanism so that it was quiet (though it would cost a bit more).

To this end, if you have a crappy hedge trimmer that you would be willing to surrender to my prototyping efforts, I would be happy to take it off your hands.

June 15, 2009

Least squares fit of a surface to a 3D cloud of points in Python (with ridiculous application)

The floor in the room above the kitchen in our house has a floor that slopes almost 1 inch per foot for half of the room. Experimentally, we have found that this is steep enough to make a desk chair roll– kind of irritating, particularly to my special ladyfriend who happens to occupy such a chair in that zone.

To compensate for the slope, I decided to fit a stack of masonite sheets to the curve of the floor. Unfortunately, the floor slopes nonlinearly in two directions, like the rounded corner of a swimming pool. After making a series of measurements of the floor, I decided to fit a polynomial in two variables to the cloud of points using a least squares estimate.

(In retrospect, the floor was close enough to singly-curved that I could have gotten away with a linear fit.) The blue thing in the picture is a level. I shimmed the level until it was worthy of its name, and then measured the distance to the floor with calipers.

Estimating the flatness of the floor

Estimating the flatness of the floor

I’ll recount the basics from my earlier post about least squares fitting in Python. Skip ahead to the next section if you read that already.

As before, the first step is to arrange the equations in canonical form: Ax=y where:

* A is an M x N full rank matrix with M > N (skinny)
* x is a vector of length N
* y is a vector of length M

As matrices, this looks like Ax = y

In polynomial fitting, A is called the Vandermonde matrix and takes the form: Vandermonde matrix

The 3D case

In the 2D case, we’re trying to find polynomial in x such that f(x) approximates y. In the 3D case at hand, we have two independent variables, so we’re looking for a polynomial in x and y such that f(x, y) approximates z. Rather than the 2D case:

we want the final output to look like this:

(Spike Curtis astutely notes in the comments that I am omitting the cross terms, such as xy, and refers us to a Matlab script. Spike is right, but the floor’s already fixed.)

We can use two Vandermonde matrices next to each other. Vandermonde ab equals z

Here’s the Python code that creates the two Vandermonde matrices and joins them into one matrix. x, y, and z are lists of corresponding coordinates, so, for example, x[5], y[5] and z[5] are the coordinates of one point that the surface should approximate. The order of the points is not important.

```python

import numpy as np

z = [0.0, 0.695, 1.345, 1.865, 2.225, 2.590, 0.0, 0.719, 1.405, 1.978, 2.398, 2.730, 0.0, 0.789, 1.474, 2.064, 2.472, 2.775, 0.0, 0.763, 1.453, 1.968, 2.356, 2.649]

x = [0.0, 12.0, 24.0, 36.0, 48.0, 60.0, 0.0, 12.0, 24.0, 36.0, 48.0, 60.0, 0.0, 12.0, 24.0, 36.0, 48.0, 60.0, 0.0, 12.0, 24.0, 36.0, 48.0, 60.0]

y = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 24.0, 24.0, 24.0, 24.0, 24.0, 24.0, 48.0, 48.0, 48.0, 48.0, 48.0, 48.0, 72.0, 72.0, 72.0, 72.0, 72.0, 72.0]

degree = 3
thickness = 0.167

# Set up the canonical least squares form
Ax = np.vander(x, degree)
Ay = np.vander(y, degree)
A = np.hstack((Ax, Ay))

# Solve for a least squares estimate
(coeffs, residuals, rank, sing_vals) = np.linalg.lstsq(A, z)

# Extract coefficients and create polynomials in x and y
xcoeffs = coeffs[0:degree]
ycoeffs = coeffs[degree:2 * degree]

fx = np.poly1d(xcoeffs)
fy = np.poly1d(ycoeffs)

```

Once I knew the coefficients of the polynomial approximation, I could calculate the contours of the masonite. From a measurement of the stack of masonite, I knew the average thickness is 0.167 inches. (Thanks to Dr. Alex T. Tung, Ph.D., for helping me get the masonite home from Home Depot.) To find out where the first layer should end, I picked a series of stations spaced every 12 inches along the length of the floor in the y-direction. Along those stations, I solved f(x, y) = 0.167, f(x, y) = 2 * 0.167, f(x, y) = 3 * 0.167 and so forth.

In practice, solving f(x, y) = c, where c is a constant, means finding the roots of the equation f(x, y) - c = 0. (The mathematicians call this solving the homogeneous equation.) In Python, the numpy.roots method solves the homogeneous case. For each contour/section crossing, I generated a polynomial of the form f(x, y) - c and solved it with numpy.roots.

```python ystations = range(0, 84, 12) sections = [[np.poly1d(xcoeffs - [0,0,zoffset - fy(ypos)]) for zoffset in np.arange(thickness, max(z), thickness).tolist()] for ypos in ystations] pts = [[min(func.roots) for func in list_of_fs] for list_of_fs in sections]

```

For fabrication, I printed out a list of the locations where the masonite contours crossed the stations.

```python for (pt_list, ystation) in zip(pts, ystations): print('\nBoundaries at station y = {0} inches:'.format(ystation)) print('\t'.join(['{0:.3}'.format(pt) for pt in pt_list]))

```

Armed with my list of measurements, I headed to the garage and set up some sawhorses with a sheet of plywood to keep the masonite from bowing and flopping around. It took a few hours of marking points and cutting gentle curves with a jigsaw, but the results were delightful.

The masonite platform (non-impressive view)

The masonite platform (non-impressive view)

Level and gleeful

Level and gleeful

Side view of the masonite platform

Side view of the masonite platform

Another side view of the masonite platform

Another side view of the masonite platform

As you can see above, I haven’t fastened the layers together yet. They seem to be sticking together reasonably well.

In the end, I think the slope went from about a 2.75″ drop across the 48″ span, to within 1/8″ of flat across the same distance. Sharon was delighted, and I found the whole project both more time-consuming and more satisfying than I expected.

June 01, 2009

Opening salvo in the Great Skunk War of 2009

Our house in Somerville has two porches that serve as barracks for at least two skunks.

Today, Sharon and I fired the first salvo, using a piece of chicken wire (AKA "hardware cloth") to prevent Merle (the commander-in-chief of the skunk army) from tunneling under the lattice work under the front porch.

Trench warfare

Historic photos from the trench warfare occurring at the northern front have reached Flickr.

Update: As of the next morning, the fortifications had not been breached! However, there was a whiff of skunk in the front hall for a few minutes. It may be that we have misjudged which side of enemy lines we were on, or more specifically, which side the enemy was on.

older postsnewer posts