Psychrometric Surface

Show off your FreeCAD projects here!
User avatar
kkremitzki
Posts: 359
Joined: Thu Mar 03, 2016 9:52 pm
Location: Texas

Psychrometric Surface

Postby kkremitzki » Sun Apr 09, 2017 7:52 am

Psychrometrics, from the Greek 'psuchron', or cold, is the study of the thermodynamic and physical properties of air and water vapor mixtures. This subject is often taught using a psychrometric chart:
psychrometric-chart.jpg
psychrometric-chart.jpg (807.26 KiB) Viewed 643 times
which is useful for calculations for not so great for teaching concepts. It may not be obvious that this chart is the projection of a surface, and e.g. the relative humidity lines are contour lines on that surface. The surface itself is the set of possible states for air & water vapor mixtures at sea-level atmospheric pressure. Paths on the surface represent thermodynamic or physical processes done on the system, and their arc lengths represent the total energy needed to change the system. In the coordinate system I've chosen, the arc length component in the +x direction represents the sensible heat load of the process, and in the +y, direction, the latent heat, and the resultant change in the Z-height of the surface represents changes in the mass ratio of water to air.

I was interested in using FreeCAD to explore this data. I have previously used the excellent thermo Python library for similar work and since it can be used in FreeCAD, I thought it was appropriate. Although traditional 3D-plotting in e.g. matplotlib might make more sense, I thought it would be an interesting thing to do. Here's the script I used to generate the set of points and insert them into a FreeCAD document as Bézier curves:

Code: Select all

from thermo.chemical import Chemical

Patm = 101325
water = Chemical('water')
T_vals = [i*2 + 273 for i in range(0, 11)] + [i*3 + 293 for i in range(0, 11)]
RH_vals = [.01] + [i/10.0 for i in range(1, 11)]
W_vals = []
doc = App.ActiveDocument

def W_from_RH(RH, Pw_sat):
    """
    ASAE D271.2 Equation 2.2.6
    The factor 0.6219 is the ratio of the molecular weights of water and air.
    """
    Pv = RH * Pw_sat
    return .6219 * Pv / (Patm - Pv)

for RH in RH_vals:
    triples = []
    for T in T_vals:
        water.T = T
        water.calculate()
        Pw_sat = water.Psat
        W = W_from_RH(RH, Pw_sat)
        W_vals.append(W)
        T = int(T)
        W = int(W * 1e3) # converting to g water/kg air
        RHpct = int(RH * 100) # converting to percentage
        triples.append((T, RH, W))
    poles = [FreeCAD.Vector(p) for p in triples]
    curve = doc.addObject('Part::Feature', 'RH' + str(RH * 100))
    bez = Part.BezierCurve()
    bez.setPoles(poles)
    curve.Shape = bez.toShape()


I then created ruled surfaces between each pair of Bézier curves, colored the surfaces according to a simple gradient, and added some explanatory text:
Screenshot from 2017-04-09 02-13-42.png
Screenshot from 2017-04-09 02-13-42.png (58.03 KiB) Viewed 643 times
Screenshot from 2017-04-09 02-13-57.png
Screenshot from 2017-04-09 02-13-57.png (68.19 KiB) Viewed 643 times


Here's a view in the same plane as the psychrometric chart. Unfortunately the lighting is not so great.
Screenshot from 2017-04-09 02-32-51.png
Screenshot from 2017-04-09 02-32-51.png (85.59 KiB) Viewed 643 times


Finally, my file is attached. I know it's not exactly the most advanced use of FreeCAD but it was a fun little distraction!
Attachments
PsychrometricSurface.fcstd
(142.92 KiB) Downloaded 11 times
damian
Posts: 200
Joined: Sun May 31, 2015 6:16 pm

Re: Psychrometric Surface

Postby damian » Sun Apr 09, 2017 8:55 am

Fantastic
User avatar
Chris_G
Posts: 379
Joined: Tue Dec 31, 2013 4:10 pm
Location: France

Re: Psychrometric Surface

Postby Chris_G » Sun Apr 09, 2017 9:45 am

Nice. And it looks good, even when not completely understanding the physics behind it :lol:
One little thing :
I guess your bezier curves are supposed to interpolate your data points.
That's not the case when doing :

Code: Select all

bez = Part.BezierCurve()
bez.setPoles(poles)
Instead you should probably do :

Code: Select all

bspline = Part.BSplineCurve()
bspline.interpolate(poles)
User avatar
kkremitzki
Posts: 359
Joined: Thu Mar 03, 2016 9:52 pm
Location: Texas

Re: Psychrometric Surface

Postby kkremitzki » Sun Apr 09, 2017 6:55 pm

Chris_G wrote:Nice. And it looks good, even when not completely understanding the physics behind it :lol:
One little thing :
I guess your bezier curves are supposed to interpolate your data points.
That's not the case when doing :

Code: Select all

bez = Part.BezierCurve()
bez.setPoles(poles)
Instead you should probably do :

Code: Select all

bspline = Part.BSplineCurve()
bspline.interpolate(poles)

Hmm, I gave that a try and got this less-than-helpful error:

Code: Select all

>>> bspline.interpolate(poles)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
Part.OCCError: Standard_ConstructionError

I examined my code and noticed my T_vals list had an overlap:

Code: Select all

>>> T_vals
[273, 275, 277, 279, 281, 283, 285, 287, 289, 291, 293, 293, 296, 299, 302, 305, 308, 311, 314, 317, 320, 323]

which was producing two duplicate points in the middle of every bspline; fixing the definition of this list resolved my issue.

Just thought I'd mention that bug/unhelpful error message.