1515
1616
1717def physical_to_voxel (point , dimensions , shape ):
18- return np .round (point / dimensions * (np .array (shape ) - 1 )).astype (int )
18+ """
19+ Map a physical coordinate in [0, L] to voxel index in [0, N-1],
20+ consistently with spacing = L/(N-1).
21+ """
22+ point = np .asarray (point , dtype = np .float64 )
23+ shape = np .asarray (shape , dtype = np .int64 )
24+ dimensions = np .asarray (dimensions , dtype = np .float64 )
25+
26+ # Map [0, L] -> [0, N-1], then clamp
27+ idx = np .floor (point / dimensions * (shape - 1 ) + 0.5 ).astype (np .int64 )
28+ idx = np .clip (idx , 0 , shape - 1 )
29+ return idx
30+
31+
32+ def _inside_cell (P , L , eps = 1e-12 ):
33+ P = np .asarray (P , float )
34+ L = np .asarray (L , float )
35+ return np .all (P >= - eps ) and np .all (P < L + eps )
1936
2037
2138def draw_strut (microstructure , start , end , radius , voxel_sizes , strut_type , L ):
22- start = np .array (start )
23- end = np .array (end )
39+ start = np .asarray (start , np .float64 )
40+ end = np .asarray (end , np .float64 )
41+ L = np .asarray (L , np .float64 )
42+
2443 direction = end - start
2544 length = np .linalg .norm (direction )
45+ if length == 0 :
46+ return
2647 direction /= length
2748
28- num_points = int ( length / np .linalg .norm (voxel_sizes ))
29- points = np . linspace ( 0 , length , num_points )
30- points = start + np .outer ( points , direction )
49+ step = np .linalg .norm (voxel_sizes ) # ~ one voxel diagonal
50+ num_points = max ( 1 , int ( np . ceil ( length / step )) )
51+ ts = np .linspace ( 0.0 , length , num_points , dtype = np . float64 )
3152
32- voxel_radius = [radius / voxel_sizes [i ] for i in range (3 )]
53+ voxel_radius = np . array ( [radius / voxel_sizes [i ] for i in range (3 )], np . float64 )
3354
34- for point in points :
35- voxel_point = physical_to_voxel (point , L , microstructure .shape )
36- x , y , z = voxel_point
37- x_min , x_max = (
38- max (0 , int (x - voxel_radius [0 ])),
39- min (microstructure .shape [0 ], int (x + voxel_radius [0 ] + 1 )),
40- )
41- y_min , y_max = (
42- max (0 , int (y - voxel_radius [1 ])),
43- min (microstructure .shape [1 ], int (y + voxel_radius [1 ] + 1 )),
44- )
45- z_min , z_max = (
46- max (0 , int (z - voxel_radius [2 ])),
47- min (microstructure .shape [2 ], int (z + voxel_radius [2 ] + 1 )),
48- )
55+ for t in ts :
56+ p = start + t * direction
57+ if not _inside_cell (p , L ):
58+ continue
59+
60+ x , y , z = physical_to_voxel (p , L , microstructure .shape )
61+
62+ x_min = max (0 , int (np .floor (x - voxel_radius [0 ])))
63+ x_max = min (microstructure .shape [0 ], int (np .ceil (x + voxel_radius [0 ] + 1 )))
64+ y_min = max (0 , int (np .floor (y - voxel_radius [1 ])))
65+ y_max = min (microstructure .shape [1 ], int (np .ceil (y + voxel_radius [1 ] + 1 )))
66+ z_min = max (0 , int (np .floor (z - voxel_radius [2 ])))
67+ z_max = min (microstructure .shape [2 ], int (np .ceil (z + voxel_radius [2 ] + 1 )))
4968
5069 if strut_type == "circle" :
5170 xx , yy , zz = np .ogrid [x_min :x_max , y_min :y_max , z_min :z_max ]
52- distance = (
53- ((xx - x ) * voxel_sizes [0 ]) ** 2
54- + ((yy - y ) * voxel_sizes [1 ]) ** 2
55- + ((zz - z ) * voxel_sizes [2 ]) ** 2
56- )
57- mask = distance <= radius ** 2
58-
59- microstructure [x_min :x_max , y_min :y_max , z_min :z_max ][mask ] = 1
71+ dx = (xx - x ) * voxel_sizes [0 ]
72+ dy = (yy - y ) * voxel_sizes [1 ]
73+ dz = (zz - z ) * voxel_sizes [2 ]
74+ mask = (dx * dx + dy * dy + dz * dz ) <= (radius * radius )
75+ microstructure [x_min :x_max , y_min :y_max , z_min :z_max ][mask ] = 1
76+ else :
77+ raise ValueError ("Only 'circle' implemented." )
6078
6179
6280def create_lattice_image (
@@ -76,14 +94,22 @@ def create_lattice_image(
7694 - microstructure: ndarray - The generated microstructure image.
7795 """
7896 if L is None :
79- L = [1 , 1 , 1 ]
97+ L = [1.0 , 1.0 , 1.0 ]
98+ L = np .asarray (L , dtype = np .float64 )
8099
81100 vertices , edges = unit_cell_func ()
82- voxel_sizes = [L [i ] / [Nx , Ny , Nz ][i ] for i in range (3 )]
101+
102+ # CONSISTENT voxel spacing with 0..N-1 indexing
103+ Nvec = np .array ([Nx , Ny , Nz ], dtype = np .int64 )
104+ voxel_sizes = L / (Nvec - 1 ).astype (np .float64 )
83105
84106 microstructure = np .zeros ((Nx , Ny , Nz ), dtype = np .int8 )
85- for edge in edges :
86- start , end = vertices [edge [0 ]] * L , vertices [edge [1 ]] * L
107+
108+ # scale vertices into physical domain [0, L]
109+ phys_vertices = vertices * L
110+
111+ for a , b in edges :
112+ start , end = phys_vertices [a ], phys_vertices [b ]
87113 draw_strut (microstructure , start , end , radius , voxel_sizes , strut_type , L )
88114
89115 return microstructure
@@ -128,7 +154,7 @@ def create_lattice_image(
128154 write_xdmf (
129155 h5_filepath = "data/lattice_microstructures.h5" ,
130156 xdmf_filepath = "data/lattice_microstructures.xdmf" ,
131- microstructure_length = L ,
157+ microstructure_length = L [:: - 1 ] ,
132158 time_series = False ,
133159 verbose = True ,
134160 )
0 commit comments