D Integration¶
This example demonstrates how to use fluids from D.
Source Code¶
1import std.stdio;
2import pyd.pyd;
3import pyd.embedded;
4import std.format;
5import std.math;
6import std.array;
7import core.time;
8import std.datetime.stopwatch;
9
10// Initialize Python before using it
11shared static this() {
12 py_init();
13}
14
15// Helper to run Python expressions and handle errors
16auto pyEval(T)(string expr, string namespace = "") {
17 try {
18 return py_eval!T(expr, namespace);
19 } catch (Exception e) {
20 writeln("Error evaluating: ", expr);
21 writeln("Error: ", e.msg);
22 throw e;
23 }
24}
25
26void testFluids() {
27 writeln("\nTesting basic fluids functionality...");
28
29 // Import fluids and create a PydObject reference
30 PydObject fluidsModule = py_import("fluids");
31 writeln("✓ Successfully imported fluids");
32
33 // Get version using attribute access
34 string fluidsVersion = fluidsModule.__version__.to_d!string();
35 writeln("✓ Fluids version: ", fluidsVersion);
36
37 double Re = pyEval!double("Reynolds(V=2.5, D=0.1, rho=1000, mu=0.001)", "fluids");
38 writeln("✓ Reynolds number calculation: ", Re);
39 assert(Re > 0);
40
41 // Test friction factor
42 double fd = pyEval!double("friction_factor(Re=1e5, eD=0.0001)", "fluids");
43 writeln("✓ Friction factor: ", fd);
44 assert(0 < fd && fd < 1);
45}
46
47void testAtmosphere() {
48 writeln("\nTesting atmosphere calculations...");
49 PydObject fluidsModule = py_import("fluids");
50 PydObject atm = py_eval("ATMOSPHERE_1976(5000.0)", "fluids");
51
52 // Get properties
53 double temp = atm.T.to_d!double();
54 double pressure = atm.P.to_d!double();
55 double density = atm.rho.to_d!double();
56 double gravity = atm.g.to_d!double();
57 double viscosity = atm.mu.to_d!double();
58 double thermalConductivity = atm.k.to_d!double();
59 double sonicVelocity = atm.v_sonic.to_d!double();
60
61 writeln("At 5000m elevation:");
62 writefln("✓ Temperature: %.4f", temp);
63 writefln("✓ Pressure: %.4f", pressure);
64 writefln("✓ Density: %.6f", density);
65 writefln("✓ Gravity: %.6f", gravity);
66 writefln("✓ Viscosity: %.6e", viscosity);
67 writefln("✓ Thermal conductivity: %.6f", thermalConductivity);
68 writefln("✓ Sonic velocity: %.4f", sonicVelocity);
69
70 // Test static methods
71 double gHigh = pyEval!double("ATMOSPHERE_1976.gravity(1E5)", "fluids");
72 writefln("✓ High altitude gravity: %.6f", gHigh);
73
74 double vSonic = pyEval!double("ATMOSPHERE_1976.sonic_velocity(300)", "fluids");
75 writefln("✓ Sonic velocity at 300K: %.4f", vSonic);
76
77 double mu400 = pyEval!double("ATMOSPHERE_1976.viscosity(400)", "fluids");
78 writefln("✓ Viscosity at 400K: %.6e", mu400);
79
80 double k400 = pyEval!double("ATMOSPHERE_1976.thermal_conductivity(400)", "fluids");
81 writefln("✓ Thermal conductivity at 400K: %.6f", k400);
82}
83
84void testTank() {
85 writeln("\nTesting tank calculations...");
86 PydObject fluidsModule = py_import("fluids");
87
88 // Test basic tank creation
89 PydObject T1 = py_eval("TANK(V=10, L_over_D=0.7, sideB='conical', horizontal=False)", "fluids");
90 writefln("✓ Tank length: %.6f", T1.L.to_d!double());
91 writefln("✓ Tank diameter: %.6f", T1.D.to_d!double());
92
93 // Test ellipsoidal tank
94 PydObject tankEllip = py_eval(
95 "TANK(D=10, V=500, horizontal=False, sideA='ellipsoidal', sideB='ellipsoidal', sideA_a=1, sideB_a=1)",
96 "fluids"
97 );
98 writefln("✓ Ellipsoidal tank L: %.6f", tankEllip.L.to_d!double());
99
100 // Test torispherical tank
101 PydObject DIN = py_eval(
102 "TANK(L=3, D=5, horizontal=False, sideA='torispherical', sideB='torispherical', " ~
103 "sideA_f=1, sideA_k=0.1, sideB_f=1, sideB_k=0.1)",
104 "fluids"
105 );
106
107 writeln("✓ Tank representation: ", DIN.toString());
108 writefln("✓ Height at V=40: %.6f", DIN.method("h_from_V", 40.0).to_d!double());
109 writefln("✓ Volume at h=4.1: %.5f", DIN.method("V_from_h", 4.1).to_d!double());
110 writefln("✓ Surface area at h=2.1: %.5f", DIN.method("SA_from_h", 2.1).to_d!double());
111
112}
113
114void testReynolds() {
115 writeln("\nTesting Reynolds number calculations:");
116
117 // Test with density and viscosity
118 double Re1 = pyEval!double("Reynolds(V=2.5, D=0.25, rho=1.1613, mu=1.9E-5)", "fluids");
119 writefln("✓ Re (with rho, mu): %.4f", Re1);
120 assert(abs(Re1 - 38200.6579) < 0.1);
121
122 // Test with kinematic viscosity
123 double Re2 = pyEval!double("Reynolds(V=2.5, D=0.25, nu=1.636e-05)", "fluids");
124 writefln("✓ Re (with nu): %.4f", Re2);
125 assert(abs(Re2 - 38202.934) < 0.1);
126}
127
128void testPSD() {
129 writeln("\nTesting particle size distribution functionality:");
130
131 // Create arrays for discrete PSD
132 double[] ds = [240, 360, 450, 562.5, 703, 878, 1097, 1371, 1713, 2141, 2676, 3345, 4181, 5226, 6532];
133 double[] numbers = [65, 119, 232, 410, 629, 849, 990, 981, 825, 579, 297, 111, 21, 1];
134
135 // Create Python lists from D arrays
136 string dsStr = format("[%(%s,%)]", ds);
137 string numbersStr = format("[%(%s,%)]", numbers);
138
139 // Create the PSD object
140 PydObject particleDist = py_eval("particle_size_distribution", "fluids");
141 PydObject psd = particleDist.ParticleSizeDistribution(ds, numbers, 0);
142 writeln("✓ Created discrete PSD");
143
144 // Test mean sizes
145 double d21 = psd.method("mean_size", 2, 1).to_d!double();
146 writefln("✓ Size-weighted mean diameter: %.4f", d21);
147
148 double d10 = psd.method("mean_size", 1, 0).to_d!double();
149 writefln("✓ Arithmetic mean diameter: %.4f", d10);
150
151 // Test percentile calculations
152 double d10Percentile = psd.method("dn", 0.1).to_d!double();
153 double d90Percentile = psd.method("dn", 0.9).to_d!double();
154 writefln("✓ D10: %.4f", d10Percentile);
155 writefln("✓ D90: %.4f", d90Percentile);
156
157 // Test probability functions
158 double pdfVal = psd.method("pdf", 1000.0).to_d!double();
159 double cdfVal = psd.method("cdf", 5000.0).to_d!double();
160 writefln("✓ PDF at 1000: %.4e", pdfVal);
161 writefln("✓ CDF at 5000: %.6f", cdfVal);
162
163 // Test lognormal distribution
164 PydObject psdLog = particleDist.PSDLognormal(0.5, 5e-6);
165 writeln("✓ Created lognormal PSD");
166
167 double vssa = psdLog.vssa.to_d!double();
168 writefln("✓ Volume specific surface area: %.2f", vssa);
169
170 // Calculate span using individual method calls
171 double dn90 = psdLog.method("dn", 0.9).to_d!double();
172 double dn10 = psdLog.method("dn", 0.1).to_d!double();
173 double span = dn90 - dn10;
174 writefln("✓ Span: %.4e", span);
175
176 // Calculate ratio using individual method calls
177 double dn75 = psdLog.method("dn", 0.75).to_d!double();
178 double dn25 = psdLog.method("dn", 0.25).to_d!double();
179 double ratio7525 = dn75 / dn25;
180 writefln("✓ D75/D25 ratio: %.6f", ratio7525);
181}
182
183void benchmarkFluids() {
184 writeln("\nRunning benchmarks:");
185
186 // Benchmark friction factor calculation
187 writeln("\nBenchmarking friction_factor:");
188 auto sw = StopWatch(AutoStart.yes);
189
190 foreach (i; 0..10000) {
191 pyEval!double("friction_factor(Re=1e5, eD=0.0001)", "fluids");
192 }
193
194 sw.stop();
195 double elapsed = sw.peek().total!"usecs" / 1_000_000.0;
196 writefln("Time for 1e4 friction_factor calls: %.6f seconds", elapsed);
197 writefln("Average time per call: %.6f seconds", elapsed/10000);
198
199 // Benchmark tank creation
200 writeln("\nBenchmarking TANK creation:");
201 sw.reset();
202 sw.start();
203
204 foreach (i; 0..1_000) {
205 py_eval(
206 "TANK(L=3, D=5, horizontal=False, sideA='torispherical', sideB='torispherical', " ~
207 "sideA_f=1, sideA_k=0.1, sideB_f=1, sideB_k=0.1)",
208 "fluids"
209 );
210 }
211
212 sw.stop();
213 elapsed = sw.peek().total!"usecs" / 1_000_000.0;
214 writefln("Average time per creation: %.6f seconds", elapsed/1_000);
215}
216
217void main() {
218 try {
219 writeln("Running fluids tests from D...");
220 testFluids();
221 testAtmosphere();
222 testTank();
223 testReynolds();
224 testPSD();
225 benchmarkFluids();
226 writeln("\nAll tests completed!");
227 } catch (Exception e) {
228 writeln("Test failed with error: ", e.msg);
229 }
230}
Requirements¶
Python with fluids installed
Usage Notes¶
The example demonstrates basic integration with fluids
15 microsecond friction factor, 36 microsecond tank creation observed by author
The script was easy to develop