Psychrometric Surface

Show off your FreeCAD projects here!
Forum rules
Be nice to others! Respect the FreeCAD code of conduct!
Post Reply
User avatar
kkremitzki
Veteran
Posts: 2511
Joined: Thu Mar 03, 2016 9:52 pm
Location: Illinois

Psychrometric Surface

Post by kkremitzki »

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 3176 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 3176 times
Screenshot from 2017-04-09 02-13-57.png
Screenshot from 2017-04-09 02-13-57.png (68.19 KiB) Viewed 3176 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 3176 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 74 times
Like my FreeCAD work? I'd appreciate any level of support via Patreon, Liberapay, or PayPal! Read more about what I do at my blog.
damian
Posts: 583
Joined: Sun May 31, 2015 6:16 pm

Re: Psychrometric Surface

Post by damian »

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

Re: Psychrometric Surface

Post by Chris_G »

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
Veteran
Posts: 2511
Joined: Thu Mar 03, 2016 9:52 pm
Location: Illinois

Re: Psychrometric Surface

Post by kkremitzki »

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.
Like my FreeCAD work? I'd appreciate any level of support via Patreon, Liberapay, or PayPal! Read more about what I do at my blog.
Post Reply