From a69b73b3c430360782f0bc714f3f39e906de49e2 Mon Sep 17 00:00:00 2001 From: AHartmaier Date: Sun, 14 Jul 2024 14:37:50 +0200 Subject: [PATCH] Added pre-defined sets for faces, edges and vertices of RVE --- src/kanapy/api.py | 2 +- src/kanapy/initializations.py | 129 ++++++++++++++++++++++++++++++++++ src/kanapy/input_output.py | 67 ++++++++++++++++-- tests/test_input_output.py | 5 +- 4 files changed, 194 insertions(+), 9 deletions(-) diff --git a/src/kanapy/api.py b/src/kanapy/api.py index d245b56c..7f7f8478 100644 --- a/src/kanapy/api.py +++ b/src/kanapy/api.py @@ -765,7 +765,7 @@ def write_abq(self, nodes=None, file=None, path='./', voxel_dict=None, grain_dic units=units, gb_area=faces, dual_phase=dual_phase, ialloy=ialloy, grain_phase_dict=grpd, - thermal=thermal) + thermal=thermal, periodic=self.rve.periodic) # if orientation exists and ialloy is defined also write material file with Euler angles if not (self.mesh.grain_ori_dict is None or ialloy is None): writeAbaqusMat(ialloy, self.mesh.grain_ori_dict, diff --git a/src/kanapy/initializations.py b/src/kanapy/initializations.py index 2c92565b..8fb1dcc3 100644 --- a/src/kanapy/initializations.py +++ b/src/kanapy/initializations.py @@ -611,3 +611,132 @@ def set_stats(grains, ar=None, omega=None, deq_min=None, deq_max=None, json.dump(ms_stats, outfile, indent=2) return ms_stats + + +class NodeSets(object): + def __init__(self, nodes): + self.F0yz = [] # left nodes + self.F1yz = [] # right nodes + self.Fx0z = [] # bottom nodes + self.Fx1z = [] # top nodes + self.Fxy0 = [] # rear nodes + self.Fxy1 = [] # front nodes + self.surfSet = [] # nodes on any surface + maxX = max(nodes[:, 0]) + minX = min(nodes[:, 0]) + maxY = max(nodes[:, 1]) + minY = min(nodes[:, 1]) + maxZ = max(nodes[:, 2]) + minZ = min(nodes[:, 2]) + # select all nodes on a surface and assign to face + for i, coord in enumerate(nodes): + surface = False + if np.isclose(coord[0], maxX): + surface = True + self.F1yz.append(i) + if np.isclose(coord[0], minX): + surface = True + self.F0yz.append(i) + if np.isclose(coord[1], maxY): + surface = True + self.Fx1z.append(i) + if np.isclose(coord[1], minY): + surface = True + self.Fx0z.append(i) + if np.isclose(coord[2], maxZ): + surface = True + self.Fxy1.append(i) + if np.isclose(coord[2], minZ): + surface = True + self.Fxy0.append(i) + if surface: + self.surfSet.append(i) + + # Find edges + # top front edge + E_T1 = np.intersect1d(self.Fxy1, self.Fx1z) + self.Ex11 = self.CreatePeriodicEdgeSets(self.surfSet, nodes[self.surfSet, 0], E_T1) + # top back edge + E_T3 = np.intersect1d(self.Fxy0, self.Fx1z) + self.Ex10 = self.CreatePeriodicEdgeSets(self.surfSet, nodes[self.surfSet, 0], E_T3) + # top left edge + E_T4 = np.intersect1d(self.F0yz, self.Fx1z) + self.E01z = self.CreatePeriodicEdgeSets(self.surfSet, nodes[self.surfSet, 2], E_T4) + # top right edge + E_T2 = np.intersect1d(self.F1yz, self.Fx1z) + self.E11z = self.CreatePeriodicEdgeSets(self.surfSet, nodes[self.surfSet, 2], E_T2) + # bottom front edge + E_B1 = np.intersect1d(self.Fxy1, self.Fx0z) + self.Ex01 = self.CreatePeriodicEdgeSets(self.surfSet, nodes[self.surfSet, 0], E_B1) + # bottom back edge + E_B3 = np.intersect1d(self.Fxy0, self.Fx0z) + self.Ex00 = self.CreatePeriodicEdgeSets(self.surfSet, nodes[self.surfSet, 0], E_B3) + # bottom left edge + E_B4 = np.intersect1d(self.F0yz, self.Fx0z) + self.E00z = self.CreatePeriodicEdgeSets(self.surfSet, nodes[self.surfSet, 2], E_B4) + # bottom right edge + E_B2 = np.intersect1d(self.F1yz, self.Fx0z) + self.E10z = self.CreatePeriodicEdgeSets(self.surfSet, nodes[self.surfSet, 2], E_B2) + # left front edge + E_M1 = np.intersect1d(self.F0yz, self.Fxy1) + self.E0y1 = self.CreatePeriodicEdgeSets(self.surfSet, nodes[self.surfSet, 1], E_M1) + # right front edge + E_M2 = np.intersect1d(self.Fxy1, self.F1yz) + self.E1y1 = self.CreatePeriodicEdgeSets(self.surfSet, nodes[self.surfSet, 1], E_M2) + # left rear edge + E_M4 = np.intersect1d(self.Fxy0, self.F0yz) + self.E0y0 = self.CreatePeriodicEdgeSets(self.surfSet, nodes[self.surfSet, 1], E_M4) + # right rear edge + E_M3 = np.intersect1d(self.Fxy0, self.F1yz) + self.E1y0 = self.CreatePeriodicEdgeSets(self.surfSet, nodes[self.surfSet, 1], E_M3) + + # find VERTICES + self.V001 = np.intersect1d(self.Ex01, self.E00z)[0] # V1 + self.V101 = np.intersect1d(self.Ex01, self.E10z)[0] # V2 + self.V000 = np.intersect1d(self.Ex00, self.E00z)[0] # H1 + self.V100 = np.intersect1d(self.E10z, self.Ex00)[0] # H2 + self.V111 = np.intersect1d(self.Ex11, self.E11z)[0] # V3 + self.V110 = np.intersect1d(self.E11z, self.Ex10)[0] # H3 + self.V011 = np.intersect1d(self.Ex11, self.E01z)[0] # V4 + self.V010 = np.intersect1d(self.Ex10, self.E01z)[0] # H4 + + # CORNERNODES = [self.V000, self.V100, self.V010, self.V001, self.V011, self.V101, self.V110, self.V111] + + def CreatePeriodicNodeSets(self, Nodes, sortCoord1, sortCoord2, NodeSet): + # Creates a List of Sorted Nodes with respect to sortCoord1 and sortCoord2 for faces + # Input: Nodeset of surface nodes + import operator + startList = [] + sortedList = [] + for number in NodeSet: + startList.append([number, sortCoord1[Nodes.index(number)], sortCoord2[Nodes.index(number)]]) + startList.sort(key=operator.itemgetter(1, 2)) + for item in range(len(startList)): + sortedList.append(startList[item][0]) + + return sortedList + + + def CreatePeriodicEdgeSets(self, Nodes, sortCoord, NodeSet): + # Creates a List of Sorted Nodes with respect to sortCoord for edges + import operator + startList = [] + sortedList = [] + for number in NodeSet: + startList.append([number, sortCoord[Nodes.index(number)]]) + startList.sort(key=operator.itemgetter(1)) + for item in range(len(startList)): + sortedList.append(startList[item][0]) + + return sortedList + + def RemoveItemInList(self, NodeList, ReplaceNodeList): + # remove set of nodes from list + for item in ReplaceNodeList: + try: + NodeList.remove(item) + except ValueError: + pass + + return NodeList + diff --git a/src/kanapy/input_output.py b/src/kanapy/input_output.py index 71dde66e..b4190b48 100644 --- a/src/kanapy/input_output.py +++ b/src/kanapy/input_output.py @@ -120,7 +120,7 @@ def read_dump(file): def export2abaqus(nodes, file, grain_dict, voxel_dict, units='mm', gb_area=None, dual_phase=False, thermal=False, - ialloy=None, grain_phase_dict=None): + ialloy=None, grain_phase_dict=None, periodic=False): r""" Creates an ABAQUS input file with microstructure morphology information in the form of nodes, elements and element sets. If "dual_phase" is true, @@ -134,19 +134,27 @@ def export2abaqus(nodes, file, grain_dict, voxel_dict, units='mm', .. note:: The nodal coordinates are written out in units of 1 mm or 1 :math:`\mu` m scale, as requested by the user in the input file. """ + from kanapy.initializations import NodeSets + + def write_node_set(name, nset): + f.write(name) + for i, val in enumerate(nset[:-1], start=1): + if i % 16 == 0: + f.write(f'{val+1}\n') + else: + f.write(f'{val+1}, ') + f.write(f'{nset[-1]+1}\n') def write_grain_sets(): # Create element sets for grains for k, v in grain_dict.items(): f.write('*ELSET, ELSET=GRAIN{0}_SET\n'.format(k)) - for enum, el in enumerate(v, 1): + for enum, el in enumerate(v[:-2], start=1): if enum % 16 != 0: - if enum == len(v): - f.write('%d\n' % el) - else: - f.write('%d, ' % el) + f.write('%d, ' % el) else: f.write('%d\n' % el) + f.write('%d\n' % v[-1]) # Create sections for k in grain_dict.keys(): if grain_phase_dict is None or grain_phase_dict[k] < nall: @@ -207,6 +215,9 @@ def write_phase_sets(): else: raise ValueError(f'Units must be either "mm" or "um", not "{0}"' .format(units)) + nsets = NodeSets(nodes) + if periodic: + p_eqs = None with open(file, 'w') as f: f.write('** Input file generated by kanapy\n') @@ -275,6 +286,49 @@ def write_phase_sets(): f.write('**\n') f.write('*Instance, name=PART-1-1, part=PART-1\n') f.write('*End Instance\n') + f.write('**\n') + f.write('** DEFINE NODE SETS\n') + f.write('** 1. VERTICES\n') + f.write(f'*Nset, nset=V000, instance=PART-1-1\n') + f.write(f'{nsets.V000+1}\n') + f.write(f'*Nset, nset=V001, instance=PART-1-1\n') + f.write(f'{nsets.V001+1}\n') + f.write(f'*Nset, nset=V010, instance=PART-1-1\n') + f.write(f'{nsets.V010+1}\n') + f.write(f'*Nset, nset=V100, instance=PART-1-1\n') + f.write(f'{nsets.V100+1}\n') + f.write(f'*Nset, nset=V011, instance=PART-1-1\n') + f.write(f'{nsets.V011+1}\n') + f.write(f'*Nset, nset=V101, instance=PART-1-1\n') + f.write(f'{nsets.V101+1}\n') + f.write(f'*Nset, nset=V110, instance=PART-1-1\n') + f.write(f'{nsets.V110+1}\n') + f.write(f'*Nset, nset=V111, instance=PART-1-1\n') + f.write(f'{nsets.V111+1}\n') + f.write('*Nset, nset=Vertices, instance=PART-1-1\n') + f.write(f'{nsets.V000+1}, {nsets.V100+1}, {nsets.V010+1}, {nsets.V001+1}, {nsets.V011+1}, ' + f'{nsets.V101+1}, {nsets.V110+1}, {nsets.V111+1}\n') + f.write('** 2. EDGES\n') + write_node_set('*Nset, nset=Ex00, instance=PART-1-1\n', nsets.Ex00) + write_node_set('*Nset, nset=Ex01, instance=PART-1-1\n', nsets.Ex01) + write_node_set('*Nset, nset=Ex10, instance=PART-1-1\n', nsets.Ex10) + write_node_set('*Nset, nset=Ex11, instance=PART-1-1\n', nsets.Ex11) + write_node_set('*Nset, nset=E0y0, instance=PART-1-1\n', nsets.E0y0) + write_node_set('*Nset, nset=E0y1, instance=PART-1-1\n', nsets.E0y1) + write_node_set('*Nset, nset=E1y0, instance=PART-1-1\n', nsets.E1y0) + write_node_set('*Nset, nset=E1y1, instance=PART-1-1\n', nsets.E1y1) + write_node_set('*Nset, nset=E00z, instance=PART-1-1\n', nsets.E00z) + write_node_set('*Nset, nset=E01z, instance=PART-1-1\n', nsets.E01z) + write_node_set('*Nset, nset=E10z, instance=PART-1-1\n', nsets.E10z) + write_node_set('*Nset, nset=E11z, instance=PART-1-1\n', nsets.E11z) + f.write('** 3. FACES\n') + write_node_set(f'*Nset, nset=Fxy0, instance=PART-1-1\n', nsets.Fxy0) + write_node_set(f'*Nset, nset=Fxy1, instance=PART-1-1\n', nsets.Fxy1) + write_node_set(f'*Nset, nset=Fx0z, instance=PART-1-1\n', nsets.Fx0z) + write_node_set(f'*Nset, nset=Fx1z, instance=PART-1-1\n', nsets.Fx1z) + write_node_set(f'*Nset, nset=F0yz, instance=PART-1-1\n', nsets.F0yz) + write_node_set(f'*Nset, nset=F1yz, instance=PART-1-1\n', nsets.F1yz) + f.write('**\n') f.write('*End Assembly\n') f.write('**\n') f.write('**\n') @@ -296,6 +350,7 @@ def write_phase_sets(): f.write('**\n') f.write('*Include, input={}mat.inp\n'.format(file[0:-8])) f.write('**\n') + f.write('**Include, input=REM_PART.inp\n') # prepare include for BC and step definitions print('---->DONE!\n') return diff --git a/tests/test_input_output.py b/tests/test_input_output.py index 0b93b611..765a94ec 100644 --- a/tests/test_input_output.py +++ b/tests/test_input_output.py @@ -65,7 +65,8 @@ def test_read_dump(temp_dump): def test_export_abaqus(): - nodes = [[1., 0., 1.], [1., 0., 0.], [0., 0., 0.], [0., 0., 1.], [1., 1., 1.], [1., 1., 0.], [0., 1., 0.], + nodes = np.array( + [[1., 0., 1.], [1., 0., 0.], [0., 0., 0.], [0., 0., 1.], [1., 1., 1.], [1., 1., 0.], [0., 1., 0.], [0., 1., 1.], [2., 0., 1.], [2., 0., 0.], [2., 1., 1.], [2., 1., 0.], [3., 0., 1.], [3., 0., 0.], [3., 1., 1.], [3., 1., 0.], [1., 2., 1.], [1., 2., 0.], [0., 2., 0.], [0., 2., 1.], [2., 2., 1.], [2., 2., 0.], [3., 2., 1.], [3., 2., 0.], [1., 3., 1.], [1., 3., 0.], [0., 3., 0.], [0., 3., 1.], @@ -74,7 +75,7 @@ def test_export_abaqus(): [2., 2., 2.], [3., 2., 2.], [1., 3., 2.], [0., 3., 2.], [2., 3., 2.], [3., 3., 2.], [1., 0., 3.], [0., 0., 3.], [1., 1., 3.], [0., 1., 3.], [2., 0., 3.], [2., 1., 3.], [3., 0., 3.], [3., 1., 3.], [1., 2., 3.], [0., 2., 3.], [2., 2., 3.], [3., 2., 3.], [1., 3., 3.], [0., 3., 3.], [2., 3., 3.], - [3., 3., 3.]] + [3., 3., 3.]]) ed = {1: [1, 2, 3, 4, 5, 6, 7, 8], 2: [9, 10, 2, 1, 11, 12, 6, 5], 3: [13, 14, 10, 9, 15, 16, 12, 11], 4: [5, 6, 7, 8, 17, 18, 19, 20], 5: [11, 12, 6, 5, 21, 22, 18, 17], 6: [15, 16, 12, 11, 23, 24, 22, 21],