# -*- coding: utf-8 -*-
#
# This file is subject to the terms and conditions defined in
# file 'LICENSE.txt', which is part of this source code package.
#
"""Ansys CDB format writer
"""
from typing import Optional, List
from io import TextIOWrapper
from pathlib import Path
import numpy as np
from Muscat.Types import MuscatIndex
import Muscat.MeshContainers.ElementsDescription as ED
from Muscat.MeshContainers.Mesh import Mesh
from Muscat.MeshContainers.Filters.FilterObjects import ElementFilter
from Muscat.IO.WriterBase import WriterBase
[docs]
def WriteMeshToCDB(fileName: str, mesh: Mesh, file: Optional[Path] = None) -> None:
"""Function API for writing mesh in the CDB format file (Ansys).
A file is created using the path and name of fileName
Parameters
----------
fileName : str
name with path to the file to be created (relative or absolute)
mesh : UnstructuredMesh
the mesh to be exported
"""
anWriter = AnsysWriter()
anWriter.Open(fileName)
if file:
anWriter.WriteFromFile(mesh, file)
else:
anWriter.Write(mesh)
anWriter.Close()
[docs]
class AnsysWriter(WriterBase):
"""Ansys CDB format writer
attributes
----------
AnsysFormulation : ["SOLID3D","SOLID2D"]
to export element with a specific physics
read the file AnsysTools.py (variable MuscatToAnsysFormulation) for more details
"""
def __init__(self) -> None:
super().__init__()
self.canHandleBinaryChange = False
self.AnsysFormulation = {3:"SOLID3D", 2:"SOLID2D"}
[docs]
def CompactIndices(self, indices: np.ndarray) -> List:
"""From a list of indices in a ansys cdb compact form
Parameters
----------
indices : np.ndarray
indices of elements one by one
Returns
-------
List
list of indices where consecutive indices are replaced by begin and -end,
and clipped to contain 8 elements or fewer
"""
arr = np.sort(indices)
diffs = np.diff(arr)
split_indices = np.where(diffs != 1)[0] + 1
sections = np.split(arr, split_indices)
idx = []
for section in sections:
if section.shape[0] > 2:
idx.extend([section[0], -section[-1]])
elif section.shape[0] == 2:
idx.extend([section[0], section[1]])
elif section.shape[0] == 1:
idx.extend([section[0]])
lines_idx = [idx[i:i+8] for i in range(0, len(idx), 8)]
return lines_idx
[docs]
def Write(self, mesh: Mesh) -> None:
"""Write cdb file from mesh information
Parameters
----------
mesh : Mesh
"""
mesh.PrepareForOutput()
self.filePointer.write("! ****** Written by Muscat package ******\n")
self.filePointer.write("! ****** Please Disconnect 'Check Valid Blocked CDB File' ******\n")
# self.filePointer.write("! ****** Named selections must be read inside mechanical by reimporting this cdb file' ******\n")
self.filePointer.write("/PREP7\n/NOPR\n")
dim = mesh.GetPointsDimensionality()
numberOfPoints = mesh.GetNumberOfNodes()
self.filePointer.write(f"NUMOFF,NODE,{numberOfPoints:>9}\n")
self.filePointer.write(f"NUMOFF,ELEM,{mesh.GetNumberOfElements(dim):>9}\n")
self.filePointer.write(f"NUMOFF,TYPE, 1\n")
self.filePointer.write("/com,*********** Nodes for the whole assembly ***********\n")
self.filePointer.write(f"NBLOCK,{dim},,{numberOfPoints}\n")
self.filePointer.write("(1i9,3e20.9e3)\n")
posn = mesh.GetPosOfNodes()
for n in range(numberOfPoints):
self.filePointer.write(f"{n+1}".rjust(9))
for d in range(3):
self.filePointer.write(f"{posn[n,d]:1.9E}".rjust(20))
self.filePointer.write("\n")
self.filePointer.write("-1\n")
exportMask = {}
exportMask[ED.Hexahedron_8] = [0, 1, 2, 3, 4, 5, 6, 7]
exportMask[ED.Wedge_6] = [0, 1, 2, 2, 3, 4, 2, 5]
exportMask[ED.Tetrahedron_4] = [0, 1, 2, 2, 3, 3, 3, 3]
exportMask[ED.Pyramid_5] = [0, 1, 2, 3, 4, 4, 5, 5]
exportMask[ED.Triangle_3] = [0, 1, 2, 2]
exportMask[ED.Quadrangle_4] = [0, 1, 2, 3]
exportMask[ED.Triangle_6] = [0, 1, 2, 3, 4, 5]
exportMask[ED.Tetrahedron_10] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
from Muscat.IO.AnsysTools import MuscatToAnsysFormulation
elcpt = 1
elemTypeNumber = 0
for ntype, elems in mesh.elements.items():
elemTypeNumber += 1
numberOfElements = elems.GetNumberOfElements()
numberOfNodesPerElement = elems.GetNumberOfNodesPerElement()
dim_elt = ED.dimensionality[ntype]
if dim == dim_elt:
self.filePointer.write(f"/com,*********** Elements for Body ***********\n")
self.filePointer.write(f"et,{elemTypeNumber},{MuscatToAnsysFormulation[self.AnsysFormulation[dim]].get(ntype,0)}\n")
mask = exportMask[ntype]
if ntype in MuscatToAnsysFormulation[self.AnsysFormulation[dim]]:
Solkey = "SOLID"
nfield = 19
header1 = "1".rjust(9) # 1 The material number
header1 += f"{elemTypeNumber}".rjust(9) # 2 The element type number
header1 += "1".rjust(9) # 3 The real constant number
header1 += "1".rjust(9) # 4 The real constant number
header1 += "0".rjust(9) # 5 Element coordinate system number
header1 += "0".rjust(9) # 6 the birth/death flag
header1 += "0".rjust(9) # 7 the solid model reference number
header1 += "0".rjust(9) # 8 the element shape flag
header1 += f"{len(mask)}".rjust(9) # 9 number of nodes defining this element if SOLID, otherwise 0
header1 += "0".rjust(9) # 10 Not used (p-elements)
header2 = ""
else:
header1 = ""
Solkey = ""
header2 = "0".rjust(9) # 2 The type of section ID.
header2 += "1".rjust(9) # 3 The real constant number
header2 += "1".rjust(9) # 4 The material number
header2 += "0".rjust(9) # 5 Element coordinate system number
self.filePointer.write(f"EBLOCK,{nfield},{Solkey},,{numberOfElements}\n")
self.filePointer.write(f"({nfield}i9)\n")
originalelcpt = elcpt
for n in range(numberOfElements):
self.filePointer.write(header1) # 1 The material number
self.filePointer.write(f"{elcpt}".rjust(9)) # 11 the element number
self.filePointer.write(header2) # 1 The material number
elcpt += 1
if numberOfNodesPerElement <= 8:
for i in mask:
self.filePointer.write(f"{elems.connectivity[n,i]+1}".rjust(9)) # 12-18 the element number
self.filePointer.write("\n")
else:
begin = ''.join(f'{str(elem):>9}' for elem in elems.connectivity[n,:8]+1)
end = ''.join(f'{str(elem):>9}' for elem in elems.connectivity[n,8:]+1)
self.filePointer.write(f"{begin}\n") # 12-18 the element number
self.filePointer.write(f"{end}\n") # 19: the element number
self.filePointer.write("-1\n")
for tag in mesh.nodesTags:
if len(tag) == 0:
continue
self.filePointer.write(f"CMBLOCK,{tag.name},NODE,{len(tag)}\n")
self.filePointer.write("(8i10)\n")
indices = tag.GetIds()
lines_idx = self.CompactIndices(indices)
for line_idx in lines_idx:
idxi = ''.join(f'{str(elem+1):>10}' for elem in line_idx)
self.filePointer.write(f"{idxi}".rjust(10))
self.filePointer.write("\n")
for ntype, elems in mesh.elements.items():
if dim == ED.dimensionality[ntype]:
for tag in elems.tags:
if len(tag) == 0:
continue
self.filePointer.write(f"CMBLOCK,{tag.name},ELEMENT,{len(tag)}\n")
self.filePointer.write("(8i10)\n")
indices = tag.GetIds()+originalelcpt
lines_idx = self.CompactIndices(indices)
for line_idx in lines_idx:
idxi = ''.join(f'{str(elem+1):>10}' for elem in line_idx)
self.filePointer.write(f"{idxi}".rjust(10))
self.filePointer.write("\n")
self.filePointer.write("/GO\nFINISH\n")
[docs]
def WriteFromFile(self, mesh: Mesh, file: Path) -> None:
"""Update an existing cdb file with new mesh information
Parameters
----------
mesh : Mesh
file : Path
Path to existing cdb file to update
"""
from Muscat.IO.AnsysTools import MuscatToAnsysFormulation
mesh.PrepareForOutput()
dim = mesh.GetPointsDimensionality()
with open(file, 'r') as infile:
lines = infile.readlines()
i = 0
while i < len(lines):
if lines[i].startswith(f"nblock,{dim},,"):
numberOfPoints = mesh.GetNumberOfNodes()
self.filePointer.write(f"nblock,{dim},,{numberOfPoints}\n")
self.filePointer.write("(1i9,3e20.9e3)\n")
posn = mesh.GetPosOfNodes()
for n in range(numberOfPoints):
self.filePointer.write(f"{n+1}".rjust(9))
for d in range(3):
self.filePointer.write(f"{posn[n,d]:1.9E}".rjust(20))
self.filePointer.write("\n")
for tag in mesh.nodesTags:
if len(tag) == 0:
continue
self.filePointer.write(f"CMBLOCK,{tag.name},NODE,{len(tag)}\n")
self.filePointer.write("(8i10)\n")
tagcpt = 0
indices = tag.GetIds()
while (tagcpt < len(tag)):
icpt = 0
while (tagcpt < len(tag)):
self.filePointer.write(f"{indices[tagcpt]+1}".rjust(10))
tagcpt += 1
self.filePointer.write("\n")
i += int(lines[i].split(",")[-1]) + 2
elif lines[i].startswith("et,1,"):
elcpt = 1
elemTypeNumber = 0
for ntype, elems in mesh.elements.items():
elemTypeNumber += 1
numberOfElements = elems.GetNumberOfElements()
numberOfNodesPerElement = elems.GetNumberOfNodesPerElement()
dim_elt = ED.dimensionality[ntype]
if dim == dim_elt:
self.filePointer.write(f"et,{elemTypeNumber},{MuscatToAnsysFormulation[self.AnsysFormulation[dim]].get(ntype,0)}\n")
self.filePointer.write(lines[i+1])
if ntype in MuscatToAnsysFormulation[self.AnsysFormulation[dim]]:
Solkey = "COMPACT"
nfield = 11
header1 = ""
header2 = ""
else:
header1 = ""
Solkey = ""
header2 = "0".rjust(9) # 2 The type of section ID.
header2 += "1".rjust(9) # 3 The real constant number
header2 += "1".rjust(9) # 4 The material number
header2 += "0".rjust(9) # 5 Element coordinate system number
self.filePointer.write(f"eblock,{nfield},{Solkey},,{numberOfElements}\n")
self.filePointer.write(f"({nfield}i9)\n")
for n in range(numberOfElements):
self.filePointer.write(header1) # 1 The material number
self.filePointer.write(f"{elcpt}".rjust(9)) # 11 the element number
self.filePointer.write(header2) # 1 The material number
elcpt += 1
for j in range(numberOfNodesPerElement):
self.filePointer.write(f"{elems.connectivity[n,j]+1}".rjust(9)) # 12-18 the element number
self.filePointer.write("\n")
i += int(lines[i+1].split(",")[-1]) + 4
else:
self.filePointer.write(lines[i])
i += 1
[docs]
def CheckIntegrity(GUI: bool = False) -> str:
from Muscat.MeshTools.MeshCreationTools import CreateCube
mesh = CreateCube(dimensions=[3, 2, 2], ofTetras=True)
# from Muscat.IO.GmshReader import ReadGmsh
# from Muscat.TestData import GetTestDataPath
# from Muscat.MeshTools.MeshInspectionTools import ExtractElementsByElementFilter
# from Muscat.MeshContainers.Filters.FilterObjects import ElementFilter
# mesh = ReadGmsh(GetTestDataPath() + "dent3D.msh")
# mesh = ExtractElementsByElementFilter(mesh, ElementFilter(dimensionality=[3,2]) )
mesh.GenerateManufacturedOriginalIDs()
from Muscat.Helpers.IO.TemporaryDirectory import TemporaryDirectory
tempdir = TemporaryDirectory.GetTempPath()
ExportElementTagsInCDBFormat(mesh, mesh.elements.GetTagsNames(), filename=tempdir+"/AnsysWriterCheckIntegrity_AnsysWriter.cdb")
WriteMeshToCDB(tempdir+"/AnsysWriterCheckIntegrity_mesh.cdb", mesh)
from Muscat.TestData import GetTestDataPath
WriteMeshToCDB(tempdir+"/AnsysWriterCheckIntegrity_mesh.cdb", mesh, GetTestDataPath() + "exemple.cdb")
return "ok"
if __name__ == '__main__':
print(CheckIntegrity(True)) # pragma: no cover