# ruby Integration

This example demonstrates how to use fluids from ruby.

## Source Code

      1require 'pycall'
      2
      3# Import fluids
      4Fluids = PyCall.import_module('fluids')
      5
      6def test_fluids
      7  begin
      8    # 1. Test basic module import
      9    puts "✓ Successfully imported fluids"
     10    puts "✓ Fluids version: #{Fluids.__version__}"
     11
     12    # 2. Test basic Reynolds number calculation
     13    re = Fluids.Reynolds(V: 2.5, D: 0.1, rho: 1000, mu: 0.001)
     14    puts "✓ Reynolds number calculation successful: #{re}"
     15    raise unless re > 0
     16
     17    # 3. Test friction factor calculation
     18    fd = Fluids.friction_factor(Re: 1e5, eD: 0.0001)
     19    puts "✓ Friction factor calculation successful: #{fd}"
     20    raise unless (0 < fd && fd < 1)
     21
     22    puts "\nAll basic tests completed successfully!"
     23  rescue => e
     24    puts "Error occurred: #{e}"
     25    raise
     26  end
     27end
     28
     29def test_atmosphere
     30  begin
     31    # Test ATMOSPHERE_1976 class
     32    atm = Fluids.ATMOSPHERE_1976.new(Z: 5000)
     33
     34    puts "\nTesting atmosphere at 5000m elevation:"
     35    puts "✓ Temperature: #{PyCall.getattr(atm, :T).round(4)}"
     36    puts "✓ Pressure: #{PyCall.getattr(atm, :P).round(4)}"
     37    puts "✓ Density: #{PyCall.getattr(atm, :rho).round(6)}"
     38
     39    # Test derived properties
     40    puts "✓ Gravity: #{PyCall.getattr(atm, :g).round(6)}"
     41    puts format("✓ Viscosity: %.6e", PyCall.getattr(atm, :mu))
     42    puts "✓ Thermal conductivity: #{PyCall.getattr(atm, :k).round(6)}"
     43    puts "✓ Sonic velocity: #{PyCall.getattr(atm, :v_sonic).round(4)}"
     44
     45    # Test static methods
     46    g_high = Fluids.ATMOSPHERE_1976.gravity(Z: 1E5)
     47    puts "✓ High altitude gravity: #{g_high.round(6)}"
     48
     49    v_sonic = Fluids.ATMOSPHERE_1976.sonic_velocity(T: 300)
     50    puts "✓ Sonic velocity at 300K: #{v_sonic.round(4)}"
     51
     52    mu_400 = Fluids.ATMOSPHERE_1976.viscosity(T: 400)
     53    puts format("✓ Viscosity at 400K: %.6e", mu_400)
     54
     55    k_400 = Fluids.ATMOSPHERE_1976.thermal_conductivity(T: 400)
     56    puts "✓ Thermal conductivity at 400K: #{k_400.round(6)}"
     57  rescue => e
     58    puts "Error in atmosphere tests: #{e}"
     59    raise
     60  end
     61end
     62
     63def test_tank
     64  begin
     65    # Test basic tank creation
     66    t1 = Fluids.TANK.new(V: 10, L_over_D: 0.7, sideB: "conical", horizontal: false)
     67    puts "\nTesting tank calculations:"
     68    puts "✓ Tank length: #{t1.L.round(6)}"
     69    puts "✓ Tank diameter: #{t1.D.round(6)}"
     70
     71    # Test ellipsoidal tank
     72    tank_ellip = Fluids.TANK.new(D: 10, V: 500, horizontal: false,
     73                            sideA: "ellipsoidal", sideB: "ellipsoidal",
     74                            sideA_a: 1, sideB_a: 1)
     75    puts "✓ Ellipsoidal tank L: #{tank_ellip.L.round(6)}"
     76
     77    # Test torispherical tank
     78    din = Fluids.TANK.new(L: 3, D: 5, horizontal: false,
     79                      sideA: "torispherical", sideB: "torispherical",
     80                      sideA_f: 1, sideA_k: 0.1, sideB_f: 1, sideB_k: 0.1)
     81
     82    puts "✓ Tank representation: #{din}"
     83    puts "✓ Tank max height: #{din.h_max.round(6)}"
     84    puts "✓ Height at V=40: #{din.h_from_V(40).round(6)}"
     85    puts "✓ Volume at h=4.1: #{din.V_from_h(4.1).round(5)}"
     86    puts "✓ Surface area at h=2.1: #{din.SA_from_h(2.1).round(5)}"
     87  rescue => e
     88    puts "Error in tank tests: #{e}"
     89    raise
     90  end
     91end
     92
     93def test_reynolds
     94  begin
     95    puts "\nTesting Reynolds number calculations:"
     96
     97    # Test with density and viscosity
     98    re1 = Fluids.Reynolds(V: 2.5, D: 0.25, rho: 1.1613, mu: 1.9E-5)
     99    puts "✓ Re (with rho, mu): #{re1.round(4)}"
    100    raise unless (re1 - 38200.6579).abs < 0.1
    101
    102    # Test with kinematic viscosity
    103    re2 = Fluids.Reynolds(V: 2.5, D: 0.25, nu: 1.636e-05)
    104    puts "✓ Re (with nu): #{re2.round(4)}"
    105    raise unless (re2 - 38202.934).abs < 0.1
    106  rescue => e
    107    puts "Error in Reynolds tests: #{e}"
    108    raise
    109  end
    110end
    111
    112def test_psd
    113  begin
    114    puts "\nTesting particle size distribution functionality:"
    115
    116    # Create a discrete PSD
    117    ds = [240, 360, 450, 562.5, 703, 878, 1097, 1371, 1713, 2141, 2676, 3345, 4181, 5226, 6532]
    118    numbers = [65, 119, 232, 410, 629, 849, 990, 981, 825, 579, 297, 111, 21, 1]
    119
    120    psd = Fluids.particle_size_distribution.ParticleSizeDistribution.new(
    121      ds: ds,
    122      fractions: numbers,
    123      order: 0
    124    )
    125    puts "✓ Created discrete PSD"
    126
    127    # Test mean sizes
    128    d21 = psd.mean_size(2, 1)
    129    puts "✓ Size-weighted mean diameter: #{d21.round(4)}"
    130    raise unless (d21 - 1857.788).abs < 0.1
    131
    132    d10 = psd.mean_size(1, 0)
    133    puts "✓ Arithmetic mean diameter: #{d10.round(4)}"
    134    raise unless (d10 - 1459.372).abs < 0.1
    135
    136    # Test percentile calculations
    137    d10_percentile = psd.dn(0.1)
    138    d90_percentile = psd.dn(0.9)
    139    puts "✓ D10: #{d10_percentile.round(4)}"
    140    puts "✓ D90: #{d90_percentile.round(4)}"
    141
    142    # Test probability functions
    143    pdf_val = psd.pdf(1000)
    144    cdf_val = psd.cdf(5000)
    145    puts "✓ PDF at 1000: %.4e" % pdf_val
    146    puts "✓ CDF at 5000: #{cdf_val.round(6)}"
    147
    148    # Test lognormal distribution
    149    psd_log = Fluids.particle_size_distribution.PSDLognormal.new(s: 0.5, d_characteristic: 5E-6)
    150    puts "✓ Created lognormal PSD"
    151
    152    vssa = psd_log.vssa
    153    puts "✓ Volume specific surface area: #{vssa.round(2)}"
    154
    155    span = psd_log.dn(0.9) - psd_log.dn(0.1)
    156    puts "✓ Span: %.4e" % span
    157
    158    ratio_7525 = psd_log.dn(0.75)/psd_log.dn(0.25)
    159    puts "✓ D75/D25 ratio: #{ratio_7525.round(6)}"
    160  rescue => e
    161    puts "Error in PSD tests: #{e}"
    162    raise
    163  end
    164end
    165
    166def benchmark_fluids
    167  puts "\nRunning benchmarks:"
    168
    169  # Benchmark friction factor calculation
    170  puts "\nBenchmarking friction_factor:"
    171  t1 = Time.now
    172  1_000_000.times do
    173    Fluids.friction_factor(Re: 1e5, eD: 0.0001)
    174  end
    175  elapsed = Time.now - t1
    176  puts "Time for 1e6 friction_factor calls: #{elapsed.round(6)} seconds"
    177  puts "Average time per call: #{(elapsed/1_000_000).round(6)} seconds"
    178
    179  # Benchmark tank creation
    180  puts "\nBenchmarking TANK creation:"
    181  t2 = Time.now
    182  1000.times do
    183    Fluids.TANK.new(L: 3, D: 5, horizontal: false,
    184                sideA: "torispherical", sideB: "torispherical",
    185                sideA_f: 1, sideA_k: 0.1, sideB_f: 1, sideB_k: 0.1)
    186  end
    187  tank_elapsed = Time.now - t2
    188  puts "Average time per creation: #{(tank_elapsed/1000).round(6)} seconds"
    189end
    190
    191# Run all tests
    192puts "Running fluids tests from Ruby..."
    193test_fluids
    194test_atmosphere
    195test_tank
    196test_reynolds
    197test_psd
    198benchmark_fluids
    199puts "\nAll tests completed!"

## Requirements

- Python with fluids installed

- pycall: https://rubygems.org/gems/pycall/versions/1.0.3

## Usage Notes

- The example demonstrates basic integration with fluids

- 4 microsecond friction factor, 35 microsecond tank creation observed by author