fortran Integration

This example demonstrates how to use fluids from fortran.

Source Code

  1module python_utils
  2  use iso_c_binding
  3  implicit none
  4
  5  ! Python object handles
  6  type(c_ptr) :: py_module = c_null_ptr
  7  type(c_ptr) :: py_none = c_null_ptr
  8  
  9contains
 10  subroutine init_python()
 11    interface
 12      subroutine Py_Initialize() bind(c, name='Py_Initialize')
 13      end subroutine
 14      
 15      function PyImport_ImportModule(name) bind(c, name='PyImport_ImportModule')
 16        use iso_c_binding
 17        character(kind=c_char), dimension(*) :: name
 18        type(c_ptr) :: PyImport_ImportModule
 19      end function
 20    end interface
 21
 22    ! Initialize Python
 23    call Py_Initialize()
 24    
 25    ! Import fluids module
 26    py_module = PyImport_ImportModule('fluids'//c_null_char)
 27    if (.not. c_associated(py_module)) then
 28      print *, 'Failed to import fluids module'
 29      call exit(1)
 30    end if
 31  end subroutine
 32
 33  subroutine test_fluids()
 34    interface
 35      function PyObject_GetAttrString(obj, name) bind(c, name='PyObject_GetAttrString')
 36        use iso_c_binding
 37        type(c_ptr), value :: obj
 38        character(kind=c_char), dimension(*) :: name
 39        type(c_ptr) :: PyObject_GetAttrString
 40      end function
 41      
 42      function PyFloat_AsDouble(obj) bind(c, name='PyFloat_AsDouble')
 43        use iso_c_binding
 44        type(c_ptr), value :: obj
 45        real(c_double) :: PyFloat_AsDouble
 46      end function
 47
 48      function PyUnicode_AsUTF8(obj) bind(c, name='PyUnicode_AsUTF8')
 49        use iso_c_binding
 50        type(c_ptr), value :: obj
 51        type(c_ptr) :: PyUnicode_AsUTF8
 52      end function
 53      
 54      function PyTuple_New(size) bind(c, name='PyTuple_New')
 55        use iso_c_binding
 56        integer(c_size_t), value :: size
 57        type(c_ptr) :: PyTuple_New
 58      end function
 59      
 60      function PyDict_New() bind(c, name='PyDict_New')
 61        use iso_c_binding
 62        type(c_ptr) :: PyDict_New
 63      end function
 64      
 65      function PyFloat_FromDouble(val) bind(c, name='PyFloat_FromDouble')
 66        use iso_c_binding
 67        real(c_double), value :: val
 68        type(c_ptr) :: PyFloat_FromDouble
 69      end function
 70      
 71      subroutine PyDict_SetItemString(dict, key, val) bind(c, name='PyDict_SetItemString')
 72        use iso_c_binding
 73        type(c_ptr), value :: dict
 74        character(kind=c_char), dimension(*) :: key
 75        type(c_ptr), value :: val
 76      end subroutine
 77      
 78      function PyObject_Call(callable, args, kwargs) bind(c, name='PyObject_Call')
 79        use iso_c_binding
 80        type(c_ptr), value :: callable
 81        type(c_ptr), value :: args
 82        type(c_ptr), value :: kwargs
 83        type(c_ptr) :: PyObject_Call
 84      end function
 85    end interface
 86
 87    type(c_ptr) :: version, reynolds_func, args, kwargs, result, str_ptr
 88    real(c_double) :: re
 89    character(len=100), pointer :: version_str
 90    character(kind=c_char), pointer :: c_str(:)
 91    character(len=100) :: temp_str
 92    integer :: i
 93
 94    print *, 'Running fluids tests from Fortran...'
 95
 96    ! Get version
 97    version = PyObject_GetAttrString(py_module, '__version__'//c_null_char)
 98    if (c_associated(version)) then
 99      str_ptr = PyUnicode_AsUTF8(version)
100      call c_f_pointer(str_ptr, c_str, [100])
101      temp_str = ''
102      i = 1
103      do while (c_str(i) /= c_null_char .and. i <= 100)
104        temp_str(i:i) = c_str(i)
105        i = i + 1
106      end do
107      
108      print *, '✓ Successfully imported fluids'
109      print *, '✓ Fluids version: ', trim(temp_str)
110    end if
111    ! Test Reynolds number calculation
112    reynolds_func = PyObject_GetAttrString(py_module, 'Reynolds'//c_null_char)
113    if (c_associated(reynolds_func)) then
114      args = PyTuple_New(0_c_size_t)
115      kwargs = PyDict_New()
116      
117      call PyDict_SetItemString(kwargs, 'V'//c_null_char, PyFloat_FromDouble(2.5d0))
118      call PyDict_SetItemString(kwargs, 'D'//c_null_char, PyFloat_FromDouble(0.1d0))
119      call PyDict_SetItemString(kwargs, 'rho'//c_null_char, PyFloat_FromDouble(1000.0d0))
120      call PyDict_SetItemString(kwargs, 'mu'//c_null_char, PyFloat_FromDouble(0.001d0))
121      
122      result = PyObject_Call(reynolds_func, args, kwargs)
123      if (c_associated(result)) then
124        re = PyFloat_AsDouble(result)
125        print *, '✓ Reynolds number calculation successful: ', re
126        if (re > 0) then
127          print *, 'Assert passed: Re > 0'
128        else
129          print *, 'Assert failed: Re <= 0'
130        end if
131      end if
132    end if
133  end subroutine
134
135  subroutine benchmark_fluids()
136    interface
137      function PyObject_GetAttrString(obj, name) bind(c, name='PyObject_GetAttrString')
138        use iso_c_binding
139        type(c_ptr), value :: obj
140        character(kind=c_char), dimension(*) :: name
141        type(c_ptr) :: PyObject_GetAttrString
142      end function
143      
144      function PyTuple_New(size) bind(c, name='PyTuple_New')
145        use iso_c_binding
146        integer(c_size_t), value :: size
147        type(c_ptr) :: PyTuple_New
148      end function
149      
150      function PyDict_New() bind(c, name='PyDict_New')
151        use iso_c_binding
152        type(c_ptr) :: PyDict_New
153      end function
154
155      function PyFloat_FromDouble(val) bind(c, name='PyFloat_FromDouble')
156        use iso_c_binding
157        real(c_double), value :: val
158        type(c_ptr) :: PyFloat_FromDouble
159      end function
160      
161      subroutine PyDict_SetItemString(dict, key, val) bind(c, name='PyDict_SetItemString')
162        use iso_c_binding
163        type(c_ptr), value :: dict
164        character(kind=c_char), dimension(*) :: key
165        type(c_ptr), value :: val
166      end subroutine
167      
168      function PyObject_Call(callable, args, kwargs) bind(c, name='PyObject_Call')
169        use iso_c_binding
170        type(c_ptr), value :: callable
171        type(c_ptr), value :: args
172        type(c_ptr), value :: kwargs
173        type(c_ptr) :: PyObject_Call
174      end function
175    end interface
176
177    type(c_ptr) :: friction_func
178    type(c_ptr) :: args, kwargs, result
179    integer :: i
180    real(c_double) :: start_time, end_time
181    
182    print *, 'Running benchmarks:'
183    
184    ! Get friction_factor function
185    friction_func = PyObject_GetAttrString(py_module, 'friction_factor'//c_null_char)
186    if (.not. c_associated(friction_func)) then
187      print *, 'Failed to get friction_factor function'
188      return
189    end if
190    
191    ! Benchmark friction_factor
192    print *, 'Benchmarking friction_factor:'
193    call cpu_time(start_time)
194    
195    do i = 1, 1000000
196      args = PyTuple_New(0_c_size_t)
197      kwargs = PyDict_New()
198      
199      call PyDict_SetItemString(kwargs, 'Re'//c_null_char, PyFloat_FromDouble(1.0d5))
200      call PyDict_SetItemString(kwargs, 'eD'//c_null_char, PyFloat_FromDouble(0.0001d0))
201      
202      result = PyObject_Call(friction_func, args, kwargs)
203    end do
204    
205    call cpu_time(end_time)
206    print *, 'Time for 1e6 friction_factor calls: ', end_time - start_time, ' seconds'
207    print *, 'Average time per call: ', (end_time - start_time) / 1000000.0, ' seconds'
208  end subroutine
209
210
211  subroutine test_tank()
212    interface
213      function PyObject_GetAttrString(obj, name) bind(c, name='PyObject_GetAttrString')
214        use iso_c_binding
215        type(c_ptr), value :: obj
216        character(kind=c_char), dimension(*) :: name
217        type(c_ptr) :: PyObject_GetAttrString
218      end function
219      
220      function PyTuple_New(size) bind(c, name='PyTuple_New')
221        use iso_c_binding
222        integer(c_size_t), value :: size
223        type(c_ptr) :: PyTuple_New
224      end function
225      
226      function PyDict_New() bind(c, name='PyDict_New')
227        use iso_c_binding
228        type(c_ptr) :: PyDict_New
229      end function
230
231      function PyFloat_FromDouble(val) bind(c, name='PyFloat_FromDouble')
232        use iso_c_binding
233        real(c_double), value :: val
234        type(c_ptr) :: PyFloat_FromDouble
235      end function
236      
237      function PyBool_FromLong(val) bind(c, name='PyBool_FromLong')
238        use iso_c_binding
239        integer(c_long), value :: val
240        type(c_ptr) :: PyBool_FromLong
241      end function
242      
243      function PyFloat_AsDouble(obj) bind(c, name='PyFloat_AsDouble')
244        use iso_c_binding
245        type(c_ptr), value :: obj
246        real(c_double) :: PyFloat_AsDouble
247      end function
248
249      function PyUnicode_FromString(str) bind(c, name='PyUnicode_FromString')
250        use iso_c_binding
251        character(kind=c_char), dimension(*) :: str
252        type(c_ptr) :: PyUnicode_FromString
253      end function
254      
255      subroutine PyDict_SetItemString(dict, key, val) bind(c, name='PyDict_SetItemString')
256        use iso_c_binding
257        type(c_ptr), value :: dict
258        character(kind=c_char), dimension(*) :: key
259        type(c_ptr), value :: val
260      end subroutine
261      
262      function PyObject_Call(callable, args, kwargs) bind(c, name='PyObject_Call')
263        use iso_c_binding
264        type(c_ptr), value :: callable
265        type(c_ptr), value :: args
266        type(c_ptr), value :: kwargs
267        type(c_ptr) :: PyObject_Call
268      end function
269
270      subroutine PyTuple_SetItem(tup, pos, item) bind(c, name='PyTuple_SetItem')
271        use iso_c_binding
272        type(c_ptr), value :: tup
273        integer(c_size_t), value :: pos
274        type(c_ptr), value :: item
275      end subroutine
276
277      function PyUnicode_AsUTF8(obj) bind(c, name='PyUnicode_AsUTF8')
278        use iso_c_binding
279        type(c_ptr), value :: obj
280        type(c_ptr) :: PyUnicode_AsUTF8
281      end function
282
283      function PyObject_CallMethod(obj, method, format) bind(c, name='PyObject_CallMethod')
284        use iso_c_binding
285        type(c_ptr), value :: obj
286        character(kind=c_char), dimension(*) :: method
287        character(kind=c_char), dimension(*) :: format
288        type(c_ptr) :: PyObject_CallMethod
289      end function
290
291      function PyObject_Str(obj) bind(c, name='PyObject_Str')
292        use iso_c_binding
293        type(c_ptr), value :: obj
294        type(c_ptr) :: PyObject_Str
295      end function
296
297      subroutine Py_DecRef(obj) bind(c, name='Py_DecRef')
298        use iso_c_binding
299        type(c_ptr), value :: obj
300      end subroutine
301
302    end interface
303
304    type(c_ptr) :: tank_class, args1, kwargs1, T1, length, diameter
305    type(c_ptr) :: tank_ellip, args_ellip, kwargs_ellip, ellip_length
306    type(c_ptr) :: args2, kwargs2, DIN, h_max, tank_str
307    type(c_ptr) :: h_from_V, V_from_h, SA_from_h
308    type(c_ptr) :: arg40, arg41, arg21, result40, result41, result21
309    real(c_double) :: tank_length, tank_diameter, ellip_l, max_height
310    real(c_double) :: height_40, volume_41, surface_area_21
311    character(kind=c_char), pointer :: c_str(:)
312    character(len=1000) :: temp_str
313    integer :: i
314    type(c_ptr) :: str_ptr
315
316    print *, 'Testing tank calculations:'
317
318    ! Get TANK class
319    tank_class = PyObject_GetAttrString(py_module, 'TANK'//c_null_char)
320    if (.not. c_associated(tank_class)) then
321      print *, 'Failed to get TANK class'
322      return
323    end if
324
325    ! Test basic tank creation
326    args1 = PyTuple_New(0_c_size_t)
327    kwargs1 = PyDict_New()
328    
329    call PyDict_SetItemString(kwargs1, 'V'//c_null_char, PyFloat_FromDouble(10.0d0))
330    call PyDict_SetItemString(kwargs1, 'L_over_D'//c_null_char, PyFloat_FromDouble(0.7d0))
331    call PyDict_SetItemString(kwargs1, 'sideB'//c_null_char, PyUnicode_FromString('conical'//c_null_char))
332    call PyDict_SetItemString(kwargs1, 'horizontal'//c_null_char, PyBool_FromLong(0_c_long))
333
334    T1 = PyObject_Call(tank_class, args1, kwargs1)
335    if (c_associated(T1)) then
336      length = PyObject_GetAttrString(T1, 'L'//c_null_char)
337      diameter = PyObject_GetAttrString(T1, 'D'//c_null_char)
338      
339      if (c_associated(length) .and. c_associated(diameter)) then
340        tank_length = PyFloat_AsDouble(length)
341        tank_diameter = PyFloat_AsDouble(diameter)
342        print *, '✓ Tank length: ', tank_length
343        print *, '✓ Tank diameter: ', tank_diameter
344      end if
345    end if
346
347    ! Test ellipsoidal tank
348    args_ellip = PyTuple_New(0_c_size_t)
349    kwargs_ellip = PyDict_New()
350    
351    call PyDict_SetItemString(kwargs_ellip, 'D'//c_null_char, PyFloat_FromDouble(10.0d0))
352    call PyDict_SetItemString(kwargs_ellip, 'V'//c_null_char, PyFloat_FromDouble(500.0d0))
353    call PyDict_SetItemString(kwargs_ellip, 'horizontal'//c_null_char, PyBool_FromLong(0_c_long))
354    call PyDict_SetItemString(kwargs_ellip, 'sideA'//c_null_char, PyUnicode_FromString('ellipsoidal'//c_null_char))
355    call PyDict_SetItemString(kwargs_ellip, 'sideB'//c_null_char, PyUnicode_FromString('ellipsoidal'//c_null_char))
356    call PyDict_SetItemString(kwargs_ellip, 'sideA_a'//c_null_char, PyFloat_FromDouble(1.0d0))
357    call PyDict_SetItemString(kwargs_ellip, 'sideB_a'//c_null_char, PyFloat_FromDouble(1.0d0))
358
359    tank_ellip = PyObject_Call(tank_class, args_ellip, kwargs_ellip)
360    if (c_associated(tank_ellip)) then
361      ellip_length = PyObject_GetAttrString(tank_ellip, 'L'//c_null_char)
362      if (c_associated(ellip_length)) then
363        ellip_l = PyFloat_AsDouble(ellip_length)
364        print *, '✓ Ellipsoidal tank L: ', ellip_l
365      end if
366    end if
367
368    ! Test torispherical tank
369    args2 = PyTuple_New(0_c_size_t)
370    kwargs2 = PyDict_New()
371    
372    call PyDict_SetItemString(kwargs2, 'L'//c_null_char, PyFloat_FromDouble(3.0d0))
373    call PyDict_SetItemString(kwargs2, 'D'//c_null_char, PyFloat_FromDouble(5.0d0))
374    call PyDict_SetItemString(kwargs2, 'horizontal'//c_null_char, PyBool_FromLong(0_c_long))
375    call PyDict_SetItemString(kwargs2, 'sideA'//c_null_char, PyUnicode_FromString('torispherical'//c_null_char))
376    call PyDict_SetItemString(kwargs2, 'sideB'//c_null_char, PyUnicode_FromString('torispherical'//c_null_char))
377    call PyDict_SetItemString(kwargs2, 'sideA_f'//c_null_char, PyFloat_FromDouble(1.0d0))
378    call PyDict_SetItemString(kwargs2, 'sideA_k'//c_null_char, PyFloat_FromDouble(0.1d0))
379    call PyDict_SetItemString(kwargs2, 'sideB_f'//c_null_char, PyFloat_FromDouble(1.0d0))
380    call PyDict_SetItemString(kwargs2, 'sideB_k'//c_null_char, PyFloat_FromDouble(0.1d0))
381
382    DIN = PyObject_Call(tank_class, args2, kwargs2)
383    if (c_associated(DIN)) then
384      ! Get string representation
385      tank_str = PyObject_Str(DIN)
386      if (c_associated(tank_str)) then
387        str_ptr = PyUnicode_AsUTF8(tank_str)
388        if (c_associated(str_ptr)) then
389          call c_f_pointer(str_ptr, c_str, [1000])
390          temp_str = ''
391          i = 1
392          do while (c_str(i) /= c_null_char .and. i <= 1000)
393            temp_str(i:i) = c_str(i)
394            i = i + 1
395          end do
396          print *, '✓ Tank representation: ', trim(temp_str)
397        end if
398      end if
399
400      ! Get max height
401      h_max = PyObject_GetAttrString(DIN, 'h_max'//c_null_char)
402      if (c_associated(h_max)) then
403        max_height = PyFloat_AsDouble(h_max)
404        print *, '✓ Tank max height: ', max_height
405      end if
406
407      ! Test h_from_V method
408      h_from_V = PyObject_GetAttrString(DIN, 'h_from_V'//c_null_char)
409      if (c_associated(h_from_V)) then
410        ! Create tuple argument
411        arg40 = PyTuple_New(1_c_size_t)
412        ! Set tuple item directly
413        call PyTuple_SetItem(arg40, 0_c_size_t, PyFloat_FromDouble(40.0d0))
414        ! Call method
415        result40 = PyObject_Call(h_from_V, arg40, c_null_ptr)
416        if (c_associated(result40)) then
417          height_40 = PyFloat_AsDouble(result40)
418          print *, '✓ Height at V=40: ', height_40
419
420          ! Cleanup
421          if (c_associated(result40)) then
422            call Py_DecRef(result40)
423          end if
424        end if
425
426        ! Cleanup
427        if (c_associated(h_from_V)) then
428          call Py_DecRef(h_from_V)
429        end if
430        if (c_associated(arg40)) then
431          call Py_DecRef(arg40)
432        end if
433      end if
434
435
436
437      ! Test SA_from_h method
438      SA_from_h = PyObject_GetAttrString(DIN, 'SA_from_h'//c_null_char)
439      if (c_associated(SA_from_h)) then
440        ! Create tuple argument
441        arg21 = PyTuple_New(1_c_size_t)
442        ! Set tuple item directly
443        call PyTuple_SetItem(arg21, 0_c_size_t, PyFloat_FromDouble(2.1d0))
444        ! Call method
445        result21 = PyObject_Call(SA_from_h, arg21, c_null_ptr)
446        if (c_associated(result21)) then
447          surface_area_21 = PyFloat_AsDouble(result21)
448          print *, '✓ Surface area at h=2.1: ', surface_area_21
449
450          ! Cleanup
451          if (c_associated(result21)) then
452            call Py_DecRef(result21)
453          end if
454        end if
455
456        ! Cleanup
457        if (c_associated(SA_from_h)) then
458          call Py_DecRef(SA_from_h)
459        end if
460        if (c_associated(arg21)) then
461          call Py_DecRef(arg21)
462        end if
463      end if
464    end if
465
466  end subroutine test_tank
467  ! Helper function to convert C string pointer to Fortran string
468  subroutine c_f_string_ptr(c_str, f_str)
469    type(c_ptr), intent(in) :: c_str
470    character(len=*), pointer, intent(out) :: f_str
471    
472    character(kind=c_char), pointer :: tmp(:)
473    integer :: i, n
474    character(len=1000) :: temp_str  ! Temporary buffer
475    
476    call c_f_pointer(c_str, tmp, [1000])  ! Limited to reasonable size
477    n = 0
478    do while (tmp(n+1) /= c_null_char .and. n < 1000)
479      n = n + 1
480      temp_str(n:n) = tmp(n)
481    end do
482    
483    allocate(character(len=n) :: f_str)
484    f_str = temp_str(1:n)
485  end subroutine
486
487subroutine test_reynolds()
488    use iso_c_binding
489    implicit none
490
491    interface
492      function PyObject_GetAttrString(obj, name) bind(c, name='PyObject_GetAttrString')
493        import :: c_ptr, c_char
494        type(c_ptr), value :: obj
495        character(kind=c_char), dimension(*) :: name
496        type(c_ptr) :: PyObject_GetAttrString
497      end function
498      
499      function PyTuple_New(size) bind(c, name='PyTuple_New')
500        import :: c_ptr, c_size_t
501        integer(c_size_t), value :: size
502        type(c_ptr) :: PyTuple_New
503      end function
504      
505      function PyDict_New() bind(c, name='PyDict_New')
506        import :: c_ptr
507        type(c_ptr) :: PyDict_New
508      end function
509
510      function PyFloat_FromDouble(val) bind(c, name='PyFloat_FromDouble')
511        import :: c_ptr, c_double
512        real(c_double), value :: val
513        type(c_ptr) :: PyFloat_FromDouble
514      end function
515      
516      function PyFloat_AsDouble(obj) bind(c, name='PyFloat_AsDouble')
517        import :: c_ptr, c_double
518        type(c_ptr), value :: obj
519        real(c_double) :: PyFloat_AsDouble
520      end function
521      
522      subroutine PyDict_SetItemString(dict, key, val) bind(c, name='PyDict_SetItemString')
523        import :: c_ptr, c_char
524        type(c_ptr), value :: dict
525        character(kind=c_char), dimension(*) :: key
526        type(c_ptr), value :: val
527      end subroutine
528      
529      function PyObject_Call(callable, args, kwargs) bind(c, name='PyObject_Call')
530        import :: c_ptr
531        type(c_ptr), value :: callable
532        type(c_ptr), value :: args
533        type(c_ptr), value :: kwargs
534        type(c_ptr) :: PyObject_Call
535      end function
536    end interface
537
538    type(c_ptr) :: reynolds_func, args, kwargs, result
539    real(c_double) :: Re1, Re2
540    logical :: test_passed
541
542    print *, 'Testing Reynolds number calculations:'
543
544    ! Get Reynolds function
545    reynolds_func = PyObject_GetAttrString(py_module, "Reynolds"//C_NULL_CHAR)
546    if (.not. c_associated(reynolds_func)) then
547      print *, 'Failed to get Reynolds function'
548      return
549    end if
550
551    ! Test with density and viscosity
552    args = PyTuple_New(0_c_size_t)
553    kwargs = PyDict_New()
554    
555    call PyDict_SetItemString(kwargs, "V"//C_NULL_CHAR, PyFloat_FromDouble(2.5d0))
556    call PyDict_SetItemString(kwargs, "D"//C_NULL_CHAR, PyFloat_FromDouble(0.25d0))
557    call PyDict_SetItemString(kwargs, "rho"//C_NULL_CHAR, PyFloat_FromDouble(1.1613d0))
558    call PyDict_SetItemString(kwargs, "mu"//C_NULL_CHAR, PyFloat_FromDouble(1.9d-5))
559    
560    result = PyObject_Call(reynolds_func, args, kwargs)
561    if (c_associated(result)) then
562      Re1 = PyFloat_AsDouble(result)
563      print *, '✓ Re (with rho, mu): ', Re1
564      
565      ! Assert abs(Re1 - 38200.6579) < 0.1
566      test_passed = abs(Re1 - 38200.6579d0) < 0.1d0
567      if (test_passed) then
568        print *, 'Assert passed: |Re1 - 38200.6579| < 0.1'
569      else
570        print *, 'Assert failed: |Re1 - 38200.6579| < 0.1'
571      end if
572    end if
573
574    ! Test with kinematic viscosity
575    args = PyTuple_New(0_c_size_t)
576    kwargs = PyDict_New()
577    
578    call PyDict_SetItemString(kwargs, "V"//C_NULL_CHAR, PyFloat_FromDouble(2.5d0))
579    call PyDict_SetItemString(kwargs, "D"//C_NULL_CHAR, PyFloat_FromDouble(0.25d0))
580    call PyDict_SetItemString(kwargs, "nu"//C_NULL_CHAR, PyFloat_FromDouble(1.636d-5))
581    
582    result = PyObject_Call(reynolds_func, args, kwargs)
583    if (c_associated(result)) then
584      Re2 = PyFloat_AsDouble(result)
585      print *, '✓ Re (with nu): ', Re2
586      
587      ! Assert abs(Re2 - 38202.934) < 0.1
588      test_passed = abs(Re2 - 38202.934d0) < 0.1d0
589      if (test_passed) then
590        print *, 'Assert passed: |Re2 - 38202.934| < 0.1'
591      else
592        print *, 'Assert failed: |Re2 - 38202.934| < 0.1'
593      end if
594    end if
595
596  end subroutine test_reynolds  
597end module
598  
599program main
600  use python_utils
601  implicit none
602  
603  call init_python()
604  call test_fluids()
605  call test_tank()
606  call test_reynolds()
607  call benchmark_fluids()
608  
609  print *, 'All tests completed!'
610end program main

Requirements

  • Python with fluids installed

Usage Notes

  • The example is incomplete. iso_c_binding is used to interface without actually writing C code.

  • The speed is comparable to pybind11, 2 microseconds per friction factor call.

  • The excess amount of code required to interface the two languages is very significant