From 6e6263e4e6eb5b45618729b7757e6003bab6e7a1 Mon Sep 17 00:00:00 2001 From: AHartmaier Date: Sun, 24 Mar 2024 07:50:38 +0100 Subject: [PATCH] Included rve statistics in API --- src/kanapy/api.py | 48 ++++++++++++++++++++++++++++------------- src/kanapy/rve_stats.py | 12 +++++------ 2 files changed, 39 insertions(+), 21 deletions(-) diff --git a/src/kanapy/api.py b/src/kanapy/api.py index fce13cc8..6a987924 100644 --- a/src/kanapy/api.py +++ b/src/kanapy/api.py @@ -25,6 +25,7 @@ from kanapy.packing import packingRoutine from kanapy.voxelization import voxelizationRoutine from kanapy.smoothingGB import smoothingRoutine +from kanapy.rve_stats import get_stats_vox, get_stats_part, get_stats_poly from kanapy.plotting import plot_init_stats, plot_voxels_3D, plot_ellipsoids_3D, \ plot_polygons_3D, plot_output_stats, plot_particles_3D @@ -164,7 +165,7 @@ def pack(self, particle_data=None, if fill_factor is None and self.precipit is not None: fill_factor = 1.0 # pack to full volume fraction defined in particles print(f'Sparse particles (precipitates/pores): ' - f'Packing up to particle volume fraction of {(100*self.precipit):.1f}%.') + f'Packing up to particle volume fraction of {(100 * self.precipit):.1f}%.') if self.precipit > 0.65: print('Overlap of particles will occur since volume fraction > 65%') self.particles, self.simbox = \ @@ -206,9 +207,9 @@ def voxelize(self, particles=None, dim=None): print('Volume fractions of phases in voxel structure:') vt = 0. for ip in range(self.nphases): - vf = vox_count[ip]/self.mesh.nvox + vf = vox_count[ip] / self.mesh.nvox vt += vf - print(f'{ip}: {self.rve.phase_names[ip]} ({(vf*100):.3f}%)') + print(f'{ip}: {self.rve.phase_names[ip]} ({(vf * 100):.3f}%)') if not np.isclose(vt, 1.0): logging.warning(f'Volume fractions of phases in voxels do not add up to 1. Value: {vt}') @@ -268,7 +269,7 @@ def generate_grains(self): igr.append(gblist[1]) if len(ind) > 0: logging.warning(f'{len(ind)} grains are not represented in polyhedral geometry.') - #logging.warning('Consider increasing the number of voxels, as grains appear to be very irregular.') + # logging.warning('Consider increasing the number of voxels, as grains appear to be very irregular.') """ind.reverse() igr.reverse() for j, i in enumerate(ind): @@ -283,7 +284,7 @@ def generate_grains(self): ph_vol[ip] += grd['Volume'] print('Volume fractions of phases in polyhedral geometry:') for ip in range(self.nphases): - vf = 100.0*ph_vol[ip]/np.prod(self.rve.size) + vf = 100.0 * ph_vol[ip] / np.prod(self.rve.size) print(f'{ip}: {self.rve.phase_names[ip]} ({vf.round(1)}%)') if empty_vox is not None: # add removed grain again @@ -351,7 +352,7 @@ def generate_orientations(self, data, ang=None, omega=None, Nbase=5000, '"random" or "unimodal"') for i, igr in enumerate(self.mesh.grain_dict.keys()): if self.mesh.grain_phase_dict[igr] == ip: - ind = i - ip*self.ngrains[0] + ind = i - ip * self.ngrains[0] ori_dict[igr] = ori_rve[ind, :] self.mesh.grain_ori_dict = ori_dict return @@ -415,7 +416,8 @@ def plot_stats(self, data=None, gs_data=None, gs_param=None, ar_data=None, ar_param=None, particles=True, - save_files=False): + save_files=False, + show_plot=True, verbose=False, ax_max=None): """ Plots the particle- and grain diameter attributes for statistical comparison.""" if self.precipit and 0 in self.mesh.grain_dict.keys(): @@ -424,13 +426,28 @@ def plot_stats(self, data=None, else: nphases = self.nphases if data is None or \ - (type(data) is str and data.lower() in ['p', 'part', 'particles']): - if self.rve.particle_data is not None: - # analyse grains w.r.t. statistical descriptors - data = get_stats(self.rve.particle_data, self.geometry, self.rve.units, - nphases, self.mesh.ngrains_phase) - if data is None: - raise ValueError('No microstructure data created yet. Run generate_grains() first.') + (type(data) is str and 'p' in data.lower()): + # analyse particle statistics + if self.particles is None: + logging.error('Particle statistics requested, but no particles defined.') + return + part_stats = get_stats_part(self.particles, show_plot=show_plot, + verbose=verbose, ax_max=ax_max, save_files=save_files) + if data is None or \ + (type(data) is str and 'v' in data.lower()): + # analyse voxel statistics + if self.mesh is None: + logging.error('Particle statistics requested, but no particles defined.') + return + vox_stats = get_stats_vox(self.mesh, show_plot=show_plot, + verbose=verbose, ax_max=ax_max, save_files=save_files) + + if self.rve.particle_data is not None: + # analyse grains w.r.t. statistical descriptors + data = get_stats(self.rve.particle_data, self.geometry, self.rve.units, + nphases, self.mesh.ngrains_phase) + if data is None: + raise ValueError('No microstructure data created yet. Run generate_grains() first.') elif type(data) is not list: data = [data] if gs_data is None or type(gs_data) is not list: @@ -937,6 +954,7 @@ def write_stl(self, data='grains', file=None, path='./'): None. """ + def write_facet(nv, pts, ft): if np.linalg.norm(nv) < 1.e-5: logging.warning(f'Acute facet detected. Facet: {ft}') @@ -955,6 +973,7 @@ def write_facet(nv, pts, ft): .format(pts[2, 0], pts[2, 1], pts[2, 2])) f.write(" endloop\n") f.write(" endfacet\n") + def write_grains(): for ft in self.geometry['Facets']: pts = self.geometry['Points'][ft] @@ -1177,7 +1196,6 @@ def import_particles(self, file, path='./'): file = os.path.join(path, file) self.simbox, self.particles = read_dump(file) - """ -------- legacy methods -------- """ diff --git a/src/kanapy/rve_stats.py b/src/kanapy/rve_stats.py index 7adf0238..9fd78bd7 100644 --- a/src/kanapy/rve_stats.py +++ b/src/kanapy/rve_stats.py @@ -208,7 +208,7 @@ def calc_stats_dict(a, b, c, eqd): return sd -def get_stats_vox(mesh, minval=1.e-5, show_plot=True, verbose=False, ax_max=None): +def get_stats_vox(mesh, minval=1.e-5, show_plot=True, verbose=False, ax_max=None, save_files=False): """ Get statistics about the microstructure from voxels, by fitting a 3D ellipsoid to each grain. @@ -315,12 +315,12 @@ def get_stats_vox(mesh, minval=1.e-5, show_plot=True, verbose=False, ax_max=None print(f'Standard deviation of equivalent grain diameter: {vox_stats_dict["eqd_sig"]:.4f}') print('--------------------------------------------------------') if show_plot: - plot_stats_dict(vox_stats_dict, title='Statistics of voxel structure') + plot_stats_dict(vox_stats_dict, title='Statistics of voxel structure', save_files=save_files) return vox_stats_dict -def get_stats_poly(grains, minval=1.e-5, show_plot=True, verbose=False, ax_max=None): +def get_stats_poly(grains, minval=1.e-5, show_plot=True, verbose=False, ax_max=None, save_files=False): """ Extract statistics about the microstructure from polyhedral grains by fitting a 3D ellipsoid to each polyhedron. @@ -394,12 +394,12 @@ def get_stats_poly(grains, minval=1.e-5, show_plot=True, verbose=False, ax_max=N print('--------------------------------------------------------') if show_plot: title = 'Statistics of polyhedral grains' - plot_stats_dict(poly_stats_dict, title=title) + plot_stats_dict(poly_stats_dict, title=title, save_files=save_files) return poly_stats_dict -def get_stats_part(part, minval=1.e-5, show_plot=True, verbose=False, ax_max=None): +def get_stats_part(part, minval=1.e-5, show_plot=True, verbose=False, ax_max=None, save_files=False): """ Extract statistics about the microstructure from particles. If inner structure is contained by fitting a 3D ellipsoid to each structure. @@ -483,7 +483,7 @@ def get_stats_part(part, minval=1.e-5, show_plot=True, verbose=False, ax_max=Non title = 'Particle statistics' else: title = 'Statistics of inner particle structures' - plot_stats_dict(part_stats_dict, title=title) + plot_stats_dict(part_stats_dict, title=title, save_files=save_files) return part_stats_dict