@@ -721,8 +721,10 @@ def plot_slice(self, cut='xy', data=None, pos=None, fname=None,
721721 """
722722
723723 def write_abq (self , nodes = None , file = None , path = './' , voxel_dict = None , grain_dict = None ,
724- dual_phase = False , thermal = False , units = None , ialloy = None , nsdv = 200 , periodicBC = False ,
725- crystal_plasticity = False , phase_props = None , value = None , apply_bc = False ):
724+ dual_phase = False , thermal = False , units = None , ialloy = None , nsdv = 200 , crystal_plasticity = False ,
725+ phase_props = None ,
726+ * ,
727+ boundary_conditions : Optional [Dict [str , Any ]] = None ):
726728 """
727729 Writes out the Abaqus deck (.inp file) for the generated RVE. The parameter nodes should be
728730 a string indicating if voxel ("v") or smoothened ("s") mesh should be written. It can also
@@ -835,9 +837,9 @@ def write_abq(self, nodes=None, file=None, path='./', voxel_dict=None, grain_dic
835837 units = units , gb_area = faces ,
836838 dual_phase = dual_phase ,
837839 ialloy = ialloy , grain_phase_dict = grpd ,
838- thermal = thermal , periodicBC = periodicBC ,
840+ thermal = thermal ,
839841 crystal_plasticity = crystal_plasticity ,
840- phase_props = phase_props , value = value , apply_bc = apply_bc )
842+ phase_props = phase_props , boundary_conditions = boundary_conditions )
841843
842844 # if orientations exist and ialloy is defined also write material file with Euler angles
843845 if not (self .mesh .grain_ori_dict is None or ialloy is None ):
@@ -1579,7 +1581,7 @@ def prompt_list(field_name: str) -> List[str]:
15791581 raise ValueError (f"Missing required metadata fields: { ', ' .join (missing )} " )
15801582
15811583 ig = {
1582- 'RVE_size' : [int (v )* length_scale for v in self .rve .size ],
1584+ 'RVE_size' : [float (v ) * length_scale for v in self .rve .size ],
15831585 'RVE_continuity' : self .rve .periodic ,
15841586 'discretization_type' : 'Structured' if structured else 'Unstructured' ,
15851587 'discretization_unit_size' : [(float (s ) / float (d )) * length_scale for s , d in zip (self .rve .size , self .rve .dim )],
@@ -1741,6 +1743,21 @@ def prompt_list(field_name: str) -> List[str]:
17411743 voxel_volume = (unit_sizes [0 ] if len (unit_sizes ) > 0 else 0.0 ) \
17421744 * (unit_sizes [1 ] if len (unit_sizes ) > 1 else 0.0 ) \
17431745 * (unit_sizes [2 ] if len (unit_sizes ) > 2 else 0.0 )
1746+
1747+ # grid size per axis
1748+ Nx , Ny , Nz = int (rve_dim [0 ]), int (rve_dim [1 ]), int (rve_dim [2 ])
1749+ # infer origin from centers if available: origin = min(center) - 0.5*unit
1750+ if len (vox_center_dict ) > 0 :
1751+ xs = [float (c [0 ])* length_scale for c in vox_center_dict .values ()]
1752+ ys = [float (c [1 ])* length_scale for c in vox_center_dict .values ()]
1753+ zs = [float (c [2 ])* length_scale for c in vox_center_dict .values ()]
1754+ ox = (min (xs ) if xs else 0.0 ) - 0.5 * unit_sizes [0 ]
1755+ oy = (min (ys ) if ys else 0.0 ) - 0.5 * unit_sizes [1 ]
1756+ oz = (min (zs ) if zs else 0.0 ) - 0.5 * unit_sizes [2 ]
1757+ else :
1758+ ox = oy = oz = 0.0
1759+ origin = [ox , oy , oz ]
1760+
17441761 # ─── precompute voxel→grain lookup ───────────────────────────────────────
17451762 voxel_to_grain = {
17461763 vid : gid
@@ -1751,7 +1768,7 @@ def prompt_list(field_name: str) -> List[str]:
17511768 grains_t0 = []
17521769 for gid in grain_phase_dict .keys ():
17531770 entry = {
1754- "gid " : gid ,
1771+ "grain_id " : gid ,
17551772 "phase_id" : grain_phase_dict .get (gid ),
17561773 "grain_volume" : len (grain_to_voxels .get (gid , [])) * voxel_volume ,
17571774 }
@@ -1762,15 +1779,36 @@ def prompt_list(field_name: str) -> List[str]:
17621779 grains_t0 .append (entry )
17631780
17641781 # ─── Build time‐0 voxel dictionary ────────────────────────────────────────
1782+ def _coord_to_index_1based (c , o , d ):
1783+ # i = round((c - o)/d + 0.5), robust to tiny float noise
1784+ return int (round ((float (c ) - float (o )) / float (d ) + 0.5 ))
1785+
1786+ def _clamp (v , lo , hi ):
1787+ return max (lo , min (hi , v ))
1788+
17651789 voxels_t0 = []
17661790 for vid , gid in voxel_to_grain .items ():
17671791 cx , cy , cz = vox_center_dict .get (vid , (0.0 , 0.0 , 0.0 ))
1792+ centroid = [float (cx ) * length_scale ,
1793+ float (cy ) * length_scale ,
1794+ float (cz ) * length_scale ]
1795+
1796+ # Compute 1-based voxel indices from centers
1797+ ix = _coord_to_index_1based (centroid [0 ], origin [0 ], unit_sizes [0 ])
1798+ iy = _coord_to_index_1based (centroid [1 ], origin [1 ], unit_sizes [1 ])
1799+ iz = _coord_to_index_1based (centroid [2 ], origin [2 ], unit_sizes [2 ])
1800+
1801+ # Clamp to valid range [1..N*]
1802+ ix = _clamp (ix , 1 , Nx )
1803+ iy = _clamp (iy , 1 , Ny )
1804+ iz = _clamp (iz , 1 , Nz )
1805+
1806+
17681807 entry = {
1769- "vid " : vid ,
1808+ "voxel_id " : vid ,
17701809 "grain_id" : gid ,
1771- "centroid_coordinates" : [float (cx ) * length_scale ,
1772- float (cy ) * length_scale ,
1773- float (cz ) * length_scale ],
1810+ "centroid_coordinates" : centroid , # scaled for output
1811+ "voxel_index" : [ix , iy , iz ], # 1-based indices
17741812 "voxel_volume" : voxel_volume ,
17751813 }
17761814 if include_orientation :
0 commit comments