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