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.
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
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
In polynomial fitting, A is called the Vandermonde matrix and takes the form:
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.
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)
Level and gleeful
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.