# 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

- Python with fluids installed

- py4cl: https://github.com/bendudson/py4cl

## Usage Notes

- The example demonstrates basic integration with fluids

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