lisp Integration

This example demonstrates how to use fluids from lisp.

Source Code

  1#!/usr/bin/sbcl --script
  2
  3;; Load quicklisp
  4#-quicklisp
  5(let ((quicklisp-init (merge-pathnames "quicklisp/setup.lisp"
  6                                     (user-homedir-pathname))))
  7  (when (probe-file quicklisp-init)
  8    (load quicklisp-init)))
  9
 10;; Load py4cl and ensure we're using Python 3
 11(ql:quickload :py4cl :silent t)
 12(setf py4cl:*python-command* "python3")
 13
 14;; Import required modules
 15;; (py4cl:import-module "fluids" :as "fl")
 16(py4cl:import-module "fluids" :as "fluids")
 17
 18;; Check Python environment
 19(py4cl:python-exec "
 20import sys
 21print('Python path:', sys.executable)
 22try:
 23    import numpy
 24    print('NumPy found at:', numpy.__file__)
 25    import fluids
 26    print('fluids found at:', fluids.__file__)
 27except ImportError as e:
 28    print('Error importing NumPy:', e)
 29")
 30
 31
 32
 33(defun test-fluids ()
 34  (handler-case
 35      (progn
 36        (format t "✓ Successfully imported fluids~%")
 37        (format t "✓ Fluids version: ~A~%" (py4cl:python-eval "fluids.__version__"))
 38        ;; Test Reynolds number calculation
 39        (let ((re (py4cl:python-eval 
 40                   (format nil "fluids.Reynolds(**{'V': ~F, 'D': ~F, 'rho': ~F, 'mu': ~F})"
 41                          2.5 0.1 1000 0.001))))
 42          (format t "✓ Reynolds number calculation successful: ~A~%" re)
 43          (assert (> re 0)))
 44        
 45        ;; Test friction factor calculation
 46        (let ((fd (py4cl:python-eval 
 47                   (format nil "fluids.friction_factor(**{'Re': ~E, 'eD': ~F})"
 48                          1e5 0.0001))))
 49          (format t "✓ Friction factor calculation successful: ~A~%" fd)
 50          (assert (and (> fd 0) (< fd 1))))
 51        
 52        (format t "~%All basic tests completed successfully!~%"))
 53    (error (e)
 54      (format t "Error occurred: ~A~%" e)
 55      (error e))))
 56      
 57      
 58      
 59(defun test-atmosphere ()
 60  (handler-case
 61      (progn
 62        ;; Create and store the atmosphere object in Python's namespace
 63        (py4cl:python-exec 
 64         (format nil "atm = fluids.ATMOSPHERE_1976(**{'Z': ~F})" 5000))
 65        
 66        (format t "~%Testing atmosphere at 5000m elevation:~%")
 67        ;; Access properties using attributes
 68        (format t "✓ Temperature: ~,4F~%" 
 69                (py4cl:python-eval "atm.T"))
 70        (format t "✓ Pressure: ~,4F~%" 
 71                (py4cl:python-eval "atm.P"))
 72        (format t "✓ Density: ~,6F~%" 
 73                (py4cl:python-eval "atm.rho"))
 74        
 75        ;; Test derived properties
 76        (format t "✓ Gravity: ~,6F~%" 
 77                (py4cl:python-eval "atm.g"))
 78        (format t "✓ Viscosity: ~,6E~%" 
 79                (py4cl:python-eval "atm.mu"))
 80        (format t "✓ Thermal conductivity: ~,6F~%" 
 81                (py4cl:python-eval "atm.k"))
 82        (format t "✓ Sonic velocity: ~,4F~%" 
 83                (py4cl:python-eval "atm.v_sonic"))
 84        
 85        ;; Test static methods
 86        (let ((g-high (py4cl:python-eval 
 87                      (format nil "fluids.ATMOSPHERE_1976.gravity(**{'Z': ~E})" 1e5))))
 88          (format t "✓ High altitude gravity: ~,6F~%" g-high))
 89        
 90        (let ((v-sonic (py4cl:python-eval 
 91                       (format nil "fluids.ATMOSPHERE_1976.sonic_velocity(**{'T': ~F})" 300))))
 92          (format t "✓ Sonic velocity at 300K: ~,4F~%" v-sonic))
 93        
 94        (let ((mu-400 (py4cl:python-eval 
 95                      (format nil "fluids.ATMOSPHERE_1976.viscosity(**{'T': ~F})" 400))))
 96          (format t "✓ Viscosity at 400K: ~,6E~%" mu-400))
 97        
 98        (let ((k-400 (py4cl:python-eval 
 99                     (format nil "fluids.ATMOSPHERE_1976.thermal_conductivity(**{'T': ~F})" 400))))
100          (format t "✓ Thermal conductivity at 400K: ~,6F~%" k-400)))
101    (error (e)
102      (format t "Error in atmosphere tests: ~A~%" e)
103      (error e))))
104      
105      
106(defun test-tank ()
107  (handler-case
108      (progn
109        ;; Test basic tank creation
110        (py4cl:python-exec 
111         (format nil "t1 = fluids.TANK(**{'V': ~F, 'L_over_D': ~F, 'sideB': 'conical', 'horizontal': False})"
112                 10 0.7))
113        (format t "~%Testing tank calculations:~%")
114        (format t "✓ Tank length: ~,6F~%" 
115                (py4cl:python-eval "t1.L"))
116        (format t "✓ Tank diameter: ~,6F~%" 
117                (py4cl:python-eval "t1.D"))
118        
119        ;; Test ellipsoidal tank
120        (py4cl:python-exec 
121         (format nil "tank_ellip = fluids.TANK(**{'D': ~F, 'V': ~F, 'horizontal': False, 
122                                                 'sideA': 'ellipsoidal', 'sideB': 'ellipsoidal',
123                                                 'sideA_a': ~F, 'sideB_a': ~F})"
124                 10 500 1 1))
125        (format t "✓ Ellipsoidal tank L: ~,6F~%" 
126                (py4cl:python-eval "tank_ellip.L"))
127        
128        ;; Test torispherical tank
129        (py4cl:python-exec 
130         (format nil "din = fluids.TANK(**{'L': ~F, 'D': ~F, 'horizontal': False,
131                                         'sideA': 'torispherical', 'sideB': 'torispherical',
132                                         'sideA_f': ~F, 'sideA_k': ~F, 'sideB_f': ~F, 'sideB_k': ~F})"
133                 3 5 1 0.1 1 0.1))
134        
135        (format t "✓ Tank representation: ~A~%" 
136                (py4cl:python-eval "str(din)"))
137        (format t "✓ Tank max height: ~,6F~%" 
138                (py4cl:python-eval "din.h_max"))
139        (format t "✓ Height at V=40: ~,6F~%" 
140                (py4cl:python-eval "din.h_from_V(40)"))
141        (format t "✓ Volume at h=4.1: ~,5F~%" 
142                (py4cl:python-eval "din.V_from_h(4.1)"))
143        (format t "✓ Surface area at h=2.1: ~,5F~%" 
144                (py4cl:python-eval "din.SA_from_h(2.1)")))
145    (error (e)
146      (format t "Error in tank tests: ~A~%" e)
147      (error e))))
148      
149      
150(defun test-psd ()
151  (handler-case
152      (progn
153        (format t "~%Testing particle size distribution functionality:~%")
154        
155        ;; Create arrays in Python's namespace
156        (py4cl:python-exec "
157ds = [240, 360, 450, 562.5, 703, 878, 1097, 1371, 1713, 2141, 2676, 3345, 4181, 5226, 6532]
158numbers = [65, 119, 232, 410, 629, 849, 990, 981, 825, 579, 297, 111, 21, 1]")
159        
160        ;; Create discrete PSD
161        (py4cl:python-exec "
162psd = fluids.particle_size_distribution.ParticleSizeDistribution(
163    ds=ds,
164    fractions=numbers,
165    order=0
166)")
167        (format t "✓ Created discrete PSD~%")
168        
169        ;; Test mean sizes
170        (let ((d21 (py4cl:python-eval "psd.mean_size(2, 1)")))
171          (format t "✓ Size-weighted mean diameter: ~,4F~%" d21)
172          (assert (< (abs (- d21 1857.788)) 0.1)))
173        
174        (let ((d10 (py4cl:python-eval "psd.mean_size(1, 0)")))
175          (format t "✓ Arithmetic mean diameter: ~,4F~%" d10)
176          (assert (< (abs (- d10 1459.372)) 0.1)))
177        
178        ;; Test percentile calculations
179        (let ((d10-percentile (py4cl:python-eval "psd.dn(0.1)"))
180              (d90-percentile (py4cl:python-eval "psd.dn(0.9)")))
181          (format t "✓ D10: ~,4F~%" d10-percentile)
182          (format t "✓ D90: ~,4F~%" d90-percentile))
183        
184        ;; Test probability functions
185        (let ((pdf-val (py4cl:python-eval "psd.pdf(1000)"))
186              (cdf-val (py4cl:python-eval "psd.cdf(5000)")))
187          (format t "✓ PDF at 1000: ~,4E~%" pdf-val)
188          (format t "✓ CDF at 5000: ~,6F~%" cdf-val))
189        
190        ;; Test lognormal distribution
191        (py4cl:python-exec 
192         (format nil "psd_log = fluids.particle_size_distribution.PSDLognormal(**{'s': ~F, 'd_characteristic': ~E})"
193                 0.5 5e-6))
194        (format t "✓ Created lognormal PSD~%")
195        
196        (let ((vssa (py4cl:python-eval "psd_log.vssa")))
197          (format t "✓ Volume specific surface area: ~,2F~%" vssa))
198        
199        (let ((span (py4cl:python-eval "psd_log.dn(0.9) - psd_log.dn(0.1)")))
200          (format t "✓ Span: ~,4E~%" span))
201        
202        (let ((ratio-7525 (py4cl:python-eval "psd_log.dn(0.75)/psd_log.dn(0.25)")))
203          (format t "✓ D75/D25 ratio: ~,6F~%" ratio-7525)))
204    (error (e)
205      (format t "Error in PSD tests: ~A~%" e)
206      (error e))))
207      
208(defun benchmark-fluids ()
209 (format t "~%Running benchmarks:~%")
210 
211 ;; Benchmark friction factor calculation
212 (format t "~%Benchmarking friction_factor:~%")
213 (let ((t1 (get-internal-real-time)))
214   (dotimes (i 10000)
215     (py4cl:python-eval 
216      (format nil "fluids.friction_factor(**{'Re': ~E, 'eD': ~F})" 1e5 0.0001)))
217   (let ((elapsed (/ (- (get-internal-real-time) t1) internal-time-units-per-second)))
218     (format t "Time for 1e6 friction_factor calls: ~,6F seconds~%" elapsed)
219     (format t "Average time per call: ~,6F seconds~%" (/ elapsed 10000))))
220 
221 ;; Benchmark tank creation
222 (format t "~%Benchmarking TANK creation:~%")
223 (let ((t2 (get-internal-real-time)))
224   (dotimes (i 10000)
225     (py4cl:python-eval 
226      (format nil "fluids.TANK(**{'L': ~F, 'D': ~F, 'horizontal': False,
227                                 'sideA': 'torispherical', 'sideB': 'torispherical',
228                                 'sideA_f': ~F, 'sideA_k': ~F, 
229                                 'sideB_f': ~F, 'sideB_k': ~F})"
230              3 5 1 0.1 1 0.1)))
231   (let ((elapsed (/ (- (get-internal-real-time) t2) internal-time-units-per-second)))
232     (format t "Average time per creation: ~,6F seconds~%" (/ elapsed 10000)))))
233     
234     
235    ;; Run all tests
236(format t "Running fluids tests from Common Lisp...~%")
237(test-fluids)
238(test-atmosphere)
239(test-tank)
240(test-psd)
241(benchmark-fluids)
242(format t "~%All tests completed!~%")
243
244;; Clean up
245(py4cl:python-stop)

Requirements

Usage Notes

  • The example demonstrates basic integration with fluids

  • 80 microsecond friction factor, 200 microsecond tank creation observed by author