Skip to content

Commit

Permalink
Unified handling of filenames and paths in export routines
Browse files Browse the repository at this point in the history
  • Loading branch information
AHartmaier committed Jan 28, 2024
1 parent f31beeb commit 902249a
Show file tree
Hide file tree
Showing 5 changed files with 163 additions and 95 deletions.
43 changes: 43 additions & 0 deletions examples/RVE_generation/create_RVE_flat_grains.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
"""
Create an RVE with oblate grains based on statistical information of a microstructure.
Author: Alexander Hartmaier
ICAMS, Ruhr University Bochum, Germany
January 2024
"""

import kanapy as knpy
from math import pi

texture = 'unimodal' # chose texture type 'random' or 'unimodal'
matname = 'Rolled' # Material name
nvox = 30 # number of voxels in each Cartesian direction
size = 40 # size of RVE in micron
periodic = True # create periodic (True) or non-periodic (False) RVE
# define statistical information on microstructure
ms_elong = {'Grain type': 'Elongated',
'Equivalent diameter': {'std': 1.0, 'mean': 12.0, 'offs': 4.0, 'cutoff_min': 8.0, 'cutoff_max': 18.0},
'Aspect ratio': {'std': 1.0, 'mean': 1.5, 'offs': -0.1, 'cutoff_min': 0.0, 'cutoff_max': 2.4},
"Tilt angle": {"std": 0.5, "mean": 0.5*pi, "cutoff_min": 0.25*pi, "cutoff_max": 0.75*pi},
'RVE': {'sideX': size, 'sideY': size, 'sideZ': size, 'Nx': nvox, 'Ny': nvox, 'Nz': nvox},
'Simulation': {'periodicity': str(periodic), 'output_units': 'um'}}

# create and visualize synthetic RVE
# create kanapy microstructure object
ms = knpy.Microstructure(descriptor=ms_elong, name=matname + '_' + texture + '_texture')
ms.init_RVE() # initialize RVE including particle distribution and structured mesh
ms.plot_stats_init() # plot initial statistics of equivalent grain diameter and aspect ratio
ms.pack() # perform particle simulation to distribute grain nuclei in RVE volume
ms.plot_ellipsoids() # plot final configuration of particles
ms.voxelize() # assign voxels to grains according to particle configuration
ms.plot_voxels(sliced=True) # plot voxels colored according to grain number
ms.generate_grains() # generate a polyhedral hull around each voxelized grain
ms.plot_grains() # plot polyhedral grains
ms.plot_stats() # compared final grain statistics with initial parameters

# create grain orientations for Goss or random texture
if knpy.MTEX_AVAIL:
ms.generate_orientations(texture, ang=[0, 45, 0], omega=7.5)

# output rve in voxel format
ms.write_voxels(script_name=__file__, mesh=False, system=False)
5 changes: 1 addition & 4 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,8 @@
if os.path.exists(adm_path):
with open(adm_path, 'r') as file:
adm_flag = file.read()
print('*** adm_flag =', adm_flag)
if '1' in adm_flag or 'true' in adm_flag.lower():
WORK_DIR = MAIN_DIR
else:
print('*** adm_flag not found.')

path_path = os.path.join(WORK_DIR, 'PATHS.json')
path_dict = {'MAIN_DIR': MAIN_DIR,
Expand All @@ -51,7 +48,7 @@

setup(
name='kanapy',
version='6.0.5',
version='6.1.0',
author='Mahesh R.G. Prasad, Abhishek Biswas, Golsa Tolooei Eshlaghi, Napat Vajragupta, Alexander Hartmaier',
author_email='[email protected]',
classifiers=[
Expand Down
161 changes: 89 additions & 72 deletions src/kanapy/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,55 +28,57 @@
plot_polygons_3D, plot_output_stats
from kanapy.util import log_level

"""
Class grain(number)
Attributes:
• phase
• voxels
• facets
• vertices
• points
• center
• simplices
• particle
• eq_dia
• maj_dia
• min_dia
• volume
• area
Class geometry()
Attributes:
• vertices
• points
• simplices
• facets
• GBnodes
• GBarea
Class particle(number)
Attributes:
• equiv_dia
• maj_dia
• min_dia
• phase
• …
Class phase(number)
Attributes:
• particles (list of numbers)
• grains (list of numbers)
"""


class Microstructure(object):
"""Define class for synthetic microstructures"""
"""Define class for synthetic microstructures
Attributes:
name : str
Name of microstructure
nphases : int
Number of phases in microstructure
ngrains : ndarray
Array of grain number in each phase
Ngr : int
Total number of grains summed over all phases
nparticles : list
List with number of particles in each phase
descriptor : list
List of dictionaries describing the microstructure of each phase;
Dict Keys: "Grains type", "Equivalent diameter", "Aspect ratio", "Tilt Angle", "RVE", "Simulation"
porosity : None or float
Indicates porosity of microstructure, if float gives volume fraction of pores
from_voxels : bool
Indicates whether microstructure object is imported from voxel file, not generated from particle simulation
particles : list
List of particle objects of class entities containing information object particle geometries
rve : object of class RVE_creator
Contains information about the RVE
Attributes: dim, size, nphases, nparticles, periodic, units, packing_steps, particle_data,
phase_names, phase_vf
simbox : Object of class Simulation_Box
Contains information about geometry of simulation box for particle simulation
mesh : object of class mesh_creator
Attributes: dim, grain_dict, grain_ori_dict, grain_phase_dict, grains, ngrains_phase. nodes, nodes_smooth,
nphases, nvox, phases, porosity_voxels, vox_center_dict, voxel_dict
geometry : dict
Dictionary of grain geometries;
Dict keys: "Ngrains", "Vertices", "Points", "Simplices", "Facets", "Grains", "GBnodes", GBarea" "GBfaces"
"Grains" : dictionary with key grain_number
Keys:"Vertices", "Points", "Center", "Simplices", "Volume", "Area", "Phase", "eqDia", "majDia", "minDia"
res_data : list
List of dictionaries containing effective microstructure descriptors for each phase in RVE
Dict keys: "Number", "Unit scale", "Grain_type",
"Grain_Equivalent_diameter", "Grain_Major_diameter", "Grain_Minor_diameter",
"Particle_Equivalent_diameter", "Particle_Major_diameter", "Particle_Minor_diameter",
"L1-error"
"""

def __init__(self, descriptor=None, file=None, name='Microstructure'):
self.name = name
self.nphases = None
self.ngrains = None
self.nparticles = None
self.nphases = None
self.porosity = None
self.rve = None
self.particles = None
Expand Down Expand Up @@ -402,49 +404,50 @@ def plot_slice(self, cut='xy', data=None, pos=None, fname=None,
-------- Import/Export methods --------
"""

def output_abq(self, nodes=None, name=None,
voxel_dict=None, grain_dict=None, faces=None,
def write_abq(self, file=None, path='./', nodes=None, voxel_dict=None, grain_dict=None,
dual_phase=False, thermal=False, units=None):
""" Writes out the Abaqus (.inp) file for the generated RVE."""
if nodes is None:
if self.mesh.nodes_smooth is not None and 'GBarea' in self.geometry.keys():
logging.warning('\nWarning: No argument "nodes" is given, will write smoothened structure')
nodes = self.mesh.nodes_smooth
faces = self.geometry['GBarea']
ntag = 'smooth'
ntag = '_smooth'
elif self.mesh.nodes is not None:
logging.warning('\nWarning: No argument "nodes" is given, will write voxelized structure')
nodes = self.mesh.nodes
faces = None
ntag = 'voxels'
ntag = '_voxels'
else:
raise ValueError('No information about voxelized microstructure. Run voxelize first.')
elif nodes in ['smooth', 's']:
elif type(nodes) is not str:
faces = None
ntag = '_voxels'
elif nodes.lower() in ['smooth', 's']:
if self.mesh.nodes_smooth is not None and 'GBarea' in self.geometry.keys():
nodes = self.mesh.nodes_smooth
faces = self.geometry['GBarea'] # use tet elements for smoothened structure
ntag = 'smooth'
ntag = '_smooth'
else:
raise ValueError('No information about smoothed microstructure. Run smoothen first.')
elif nodes in ['voxels', 'v', 'voxel']:
elif nodes.lower() in ['voxels', 'v', 'voxel']:
if self.mesh.nodes is not None:
nodes = self.mesh.nodes
faces = None # use brick elements for voxel structure
ntag = 'voxels'
ntag = '_voxels'
else:
raise ValueError('No information about voxelized microstructure. Run voxelize first.')
else:
raise ValueError('Wrong value for parameter "nodes". Must be either "smooth" ' +
f'or "voxels", not {nodes}')

if voxel_dict is None:
voxel_dict = self.mesh.voxel_dict
if units is None:
units = self.rve.units
elif (not units == 'mm') and (not units == 'um'):
raise ValueError(f'Units must be either "mm" or "um", not {units}.')
if dual_phase:
nct = 'dual_phase'
nct = 'abq_dual_phase'
if grain_dict is None:
grain_dict = dict()
for i in range(self.nphases):
Expand All @@ -455,16 +458,18 @@ def output_abq(self, nodes=None, name=None,
else:
if grain_dict is None:
grain_dict = self.mesh.grain_dict
nct = '{0}grains'.format(len(grain_dict))
if name is None:
cwd = os.getcwd()
name = os.path.normpath(cwd + f'/kanapy_{nct}_{ntag}.inp')
if os.path.exists(name):
os.remove(name) # remove old file if it exists
export2abaqus(nodes, name, grain_dict, voxel_dict,
nct = f'abq_px_{len(grain_dict)}'
if file is None:
if self.name == 'Microstructure':
file = nct + ntag + '_geom.inp'
else:
file = self.name + ntag + '_geom.inp'
path = os.path.normpath(path)
file = os.path.join(path, file)
export2abaqus(nodes, file, grain_dict, voxel_dict,
units=units, gb_area=faces,
dual_phase=dual_phase, thermal=thermal)
return name
return file

# def output_neper(self, timestep=None):
def output_neper(self):
Expand Down Expand Up @@ -742,7 +747,7 @@ def output_ang(self, ori=None, cut='xy', data=None, plot=True, cs=None,
plt.show()
return fname

def write_stl(self, file=None):
def write_stl(self, file=None, path='./'):
""" Write triangles of convex polyhedra forming grains in form of STL
files in the format
'
Expand All @@ -761,12 +766,13 @@ def write_stl(self, file=None):
-------
None.
"""

if file is None:
if self.name == 'Microstructure':
file = 'px_{}grains.stl'.format(self.Ngr)
else:
file = self.name + '.stl'
path = os.path.normpath(path)
file = os.path.join(path, file)
with open(file, 'w') as f:
f.write("solid {}\n".format(self.name))
for ft in self.geometry['Facets']:
Expand All @@ -792,12 +798,15 @@ def write_stl(self, file=None):
f.write("endsolid\n")
return

def write_centers(self, file=None, grains=None):
def write_centers(self, file=None, path='./', grains=None):
"""Write grain center positions into CSV file."""
if file is None:
if self.name == 'Microstructure':
file = 'px_{}grains_centroid.csv'.format(self.Ngr)
else:
file = self.name + '_centroid.csv'
path = os.path.normpath(path)
file = os.path.join(path, file)
if grains is None:
grains = self.geometry['Grains']
with open(file, 'w') as f:
Expand All @@ -807,12 +816,14 @@ def write_centers(self, file=None, grains=None):
f.write('{}, {}, {}\n'.format(ctr[0], ctr[1], ctr[2]))
return

def write_ori(self, angles=None, file=None):
def write_ori(self, angles=None, file=None, path='./'):
if file is None:
if self.name == 'Microstructure':
file = 'px_{}grains_ori.csv'.format(self.Ngr)
else:
file = self.name + '_ori.csv'
path = os.path.normpath(path)
file = os.path.join(path, file)
if angles is None:
if self.mesh.grain_ori_dict is None:
raise ValueError('No grain orientations given or stored.')
Expand Down Expand Up @@ -850,14 +861,13 @@ def write_voxels(self, angles=None, script_name=None, file=None, path='./',

if script_name is None:
script_name = __file__
if path[-1] != '/':
path += '/'
if file is None:
if self.name == 'Microstructure':
file = path + 'px_{}grains_voxels.json'.format(self.Ngr)
file = f'px_{self.Ngr}grains_voxels.json'
else:
file = path + self.name + '_voxels.json'
file = os.path.normpath(file)
file = self.name + '_voxels.json'
path = os.path.normpath(path)
file = os.path.join(path, file)
print(f'Writing voxel information of microstructure to {file}.')
# metadata
today = str(date.today()) # date
Expand Down Expand Up @@ -960,14 +970,13 @@ def pckl(self, file=None, path='./'):
"""
import pickle

if path[-1] != '/':
path += '/'
if file is None:
if self.name == 'Microstructure':
file = 'px_{}grains_microstructure.pckl'.format(self.Ngr)
else:
file = self.name + '_microstructure.pckl'
file = os.path.normpath(path + file)
path = os.path.normpath(path)
file = os.path.join(path, file)
with open(file, 'wb') as output:
pickle.dump(self, output, pickle.HIGHEST_PROTOCOL)
return
Expand All @@ -980,3 +989,11 @@ def init_stats(self, descriptor=None, gs_data=None, ar_data=None, porous=False,
""" Legacy function for plot_stats_init."""
logging.warning('This legacy function is depracted, please use "plot_stats_init()".')
self.plot_stats_init(descriptor, gs_data=gs_data, ar_data=ar_data, porous=porous, save_files=save_files)

def output_abq(self, nodes=None, name=None,
voxel_dict=None, grain_dict=None, faces=None,
dual_phase=False, thermal=False, units=None):
""" Legacy function for write_abq."""
logging.warning('This legacy function is depracted, please use "write_abq()".')
self.write_abq(nodes=nodes, name=name, voxel_dict=voxel_dict, grain_dict=grain_dict, faces=faces,
dual_phase=dual_phase, thermal=thermal, units=units)
Loading

0 comments on commit 902249a

Please sign in to comment.