## Psychrometric Surface

kkremitzki
Posts: 359
Joined: Thu Mar 03, 2016 9:52 pm
Location: Texas

### Psychrometric Surface

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 (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 ChemicalPatm = 101325water = 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.ActiveDocumentdef 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 (58.03 KiB) Viewed 643 times
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 (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
damian
Posts: 200
Joined: Sun May 31, 2015 6:16 pm

### Re: Psychrometric Surface

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

### Re: Psychrometric Surface

Nice. And it looks good, even when not completely understanding the physics behind it
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)`
kkremitzki
Posts: 359
Joined: Thu Mar 03, 2016 9:52 pm
Location: Texas

### Re: Psychrometric Surface

Chris_G wrote:Nice. And it looks good, even when not completely understanding the physics behind it
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.