diff --git a/src/ifc2ca/ca2ifc.py b/src/ifc2ca/ca2ifc.py new file mode 100644 index 0000000000..7b0568ba1e --- /dev/null +++ b/src/ifc2ca/ca2ifc.py @@ -0,0 +1,291 @@ +import json +import ifcopenshell +import os + +class CA2IFC: + def __init__(self, inputFilename, outputFilename): + self.inputFilename = inputFilename + self.outputFilename = outputFilename + self.data = None + self.f = None + self.reps = {} + self.origin = None + self.xAxis = None + self.yAxis = None + self.zAxis = None + + def convert(self): + # load json file + with open(self.inputFilename) as dataFile: + self.data = json.load(dataFile) + + # initiate ifc file + self.f = ifcopenshell.file() + + # create header + self.create_header() + + # create global axes + globalAxes = self.create_global_axes() + localPlacement = self.f.createIfcLocalPlacement(None, globalAxes) + + # TODO: create units + unitAssignment = self.f.createIfcUnitAssignment() + + # create owner history + ownerHistory = self.create_owner_history() + + # create representations and subrepresentations + self.reps = self.create_reference_subrep(globalAxes) + + # create project and model + project = self.f.createIfcProject(self.guid(), ownerHistory, 'A Project', None, None, None, None, (self.reps['model'],), unitAssignment) + model = self.f.createIfcStructuralAnalysisModel(self.guid(), ownerHistory, self.data['name'], None, None, 'NOTDEFINED', globalAxes, None, None, localPlacement) + self.f.createIfcRelDeclares(self.guid(), ownerHistory, None, None, project, (model,)) + + # create materials + ifcMaterials = [None for _ in range(len(self.data['db']['materials']))] + for i,material in enumerate(self.data['db']['materials']): + ifcMaterials[i] = self.create_material(material) + + # create profiles + ifcProfiles = [None for _ in range(len(self.data['db']['profiles']))] + for i,profile in enumerate(self.data['db']['profiles']): + ifcProfiles[i] = self.create_profile(profile) + + # create material-profile sets + mpSets = list(set([el['material'] + '-' + el['profile'] for el in self.data['elements'] if el['geometryType'] == 'line'])) + ifcMaterialProfileSets = [None for _ in range(len(mpSets))] + for i,mpSet in enumerate(mpSets): + materialIndex = [mat['ifcName'] for mat in self.data['db']['materials']].index(mpSet.split('-')[0]) + profileIndex = [prof['ifcName'] for prof in self.data['db']['profiles']].index(mpSet.split('-')[1]) + material = ifcMaterials[materialIndex] + profile = ifcProfiles[materialIndex] + matProf = self.f.createIfcMaterialProfile(self.data['db']['materials'][materialIndex]['name'] + ' | ' + self.data['db']['profiles'][profileIndex]['profileName'], None, material, profile) + ifcMaterialProfileSets[i] = self.f.createIfcMaterialProfileSet(None, None, (matProf,)) + + # create structural elements + ifcElements = [None for _ in range(len(self.data['elements']))] + for i,el in enumerate(self.data['elements']): + # geometry - product definition shape + prodDefShape = self.create_geometry(el) + + if el['geometryType'] == 'line': + # z axis TODO: group by elements + localZAxis = self.f.createIfcDirection(tuple(el['orientation'][2])) + # element + ifcElements[i] = self.f.createIfcStructuralCurveMember(self.guid(), ownerHistory, el['name'], None, None, localPlacement, prodDefShape, el['predefinedType'], localZAxis) + + if el['geometryType'] == 'surface': + # element + ifcElements[i] = self.f.createIfcStructuralSurfaceMember(self.guid(), ownerHistory, el['name'], None, None, localPlacement, prodDefShape, el['predefinedType'], el['thickness']) + + # create structural point connections + ifcConnections = [None for _ in range(len(self.data['connections']))] + for i,conn in enumerate(self.data['connections']): + # geometry - product definition shape + prodDefShape = self.create_geometry(conn) + # local axes + localAxes = self.create_orientation(conn['orientation']) + # boundary conditions + if conn['appliedCondition']: + bc = conn['appliedCondition'] + appliedCondition = self.f.createIfcBoundaryNodeCondition(None, self.f.createIfcBoolean(bc['dx']), self.f.createIfcBoolean(bc['dy']), self.f.createIfcBoolean(bc['dz']), self.f.createIfcBoolean(bc['drx']), self.f.createIfcBoolean(bc['dry']), self.f.createIfcBoolean(bc['drz'])) + else: + appliedCondition = None + + # connection + if conn['geometryType'] == 'point': + ifcConnections[i] = self.f.createIfcStructuralPointConnection(self.guid(), ownerHistory, conn['name'], None, None, localPlacement, prodDefShape, appliedCondition, localAxes) + + # assign material-profile-sets + for i,mpSet in enumerate(mpSets): + groupOfElements = [] + for j,el in enumerate(self.data['elements']): + if el['geometryType'] == 'line' and el['material'] + '-' + el['profile'] == mpSet: + groupOfElements.append(ifcElements[j]) + + if groupOfElements: + self.f.createIfcRelAssociatesMaterial(self.guid(), ownerHistory, None, None, tuple(groupOfElements), ifcMaterialProfileSets[i]) + + # assign materials + for i,mat in enumerate(self.data['db']['materials']): + groupOfElements = [] + for j,el in enumerate(self.data['elements']): + if el['geometryType'] == 'surface' and el['material'] == mat['ifcName']: + groupOfElements.append(ifcElements[j]) + if groupOfElements: + self.f.createIfcRelAssociatesMaterial(self.guid(), ownerHistory, None, None, tuple(groupOfElements), ifcMaterials[i]) + + # create connections with elements + for i,el in enumerate(self.data['elements']): + for conn in el['connections']: + localAxes = self.create_orientation(conn['orientation']) + if conn['appliedCondition']: + bc = conn['appliedCondition'] + appliedCondition = self.f.createIfcBoundaryNodeCondition(None, self.f.createIfcBoolean(bc['dx']), self.f.createIfcBoolean(bc['dy']), self.f.createIfcBoolean(bc['dz']), self.f.createIfcBoolean(bc['drx']), self.f.createIfcBoolean(bc['dry']), self.f.createIfcBoolean(bc['drz'])) + else: + appliedCondition = None + j = [c['ifcName'] for c in self.data['connections']].index(conn['relatedConnection']) + if not conn['eccentricity']: + self.f.createIfcRelConnectsStructuralMember(self.guid(), ownerHistory, None, None, ifcElements[i], ifcConnections[j], appliedCondition, None, None, localAxes) + else: + pass + + # assign elements and connections to group + self.f.createIfcRelAssignsToGroup(self.guid(), ownerHistory, None, None, tuple(ifcElements + ifcConnections), None, model) + + # finalize ifc file + self.f.write(self.outputFilename) + + def guid(self): + return ifcopenshell.guid.new() + + def create_header(self): + self.f.wrapped_data.header.file_name.name = os.path.basename(self.outputFilename) + + def create_global_axes(self): + self.xAxis = self.f.createIfcDirection((1., 0., 0.)) + self.yAxis = self.f.createIfcDirection((0., 1., 0.)) + self.zAxis = self.f.createIfcDirection((0., 0., 1.)) + self.origin = self.f.createIfcCartesianPoint((0., 0., 0.)) + axes = self.f.createIfcAxis2Placement3D(self.origin, self.zAxis, self.xAxis) + + return axes + + def create_orientation(self, orientation): + xAxis = self.f.createIfcDirection(tuple(orientation[0])) + zAxis = self.f.createIfcDirection(tuple(orientation[2])) + axes = self.f.createIfcAxis2Placement3D(self.origin, zAxis, xAxis) + + return axes + + def create_owner_history(self): + actor = self.f.createIfcActorRole('ENGINEER', None, None) + person = self.f.createIfcPerson('Christovasilis', None, 'Ioannis', None, None, None, (actor,)) + organization = self.f.createIfcOrganization(None, 'IfcOpenShell', 'IfcOpenShell, an open source (LGPL) software library that helps users and software developers to work with the IFC file format.') + p_o = self.f.createIfcPersonAndOrganization(person, organization) + application = self.f.createIfcApplication(organization, 'v0.0.x', 'IFC2CA', 'IFC2CA') + ownerHistory = self.f.createIfcOwnerHistory(p_o, application, 'READWRITE', None, None, p_o, application) + + return ownerHistory + + def create_reference_subrep(self, globalAxes): + modelRep = self.f.createIfcGeometricRepresentationContext(None, 'Model', 3, 1.E-05, globalAxes, None) + bodySubRep = self.f.createIfcGeometricRepresentationSubContext('Body', 'Model', None, None, None , None, modelRep, None, 'MODEL_VIEW', None) + refSubRep = self.f.createIfcGeometricRepresentationSubContext('Reference', 'Model', None, None, None , None, modelRep, None, 'GRAPH_VIEW', None) + + return { + 'model': modelRep, + 'body': bodySubRep, + 'reference': refSubRep + } + + def create_material(self, material): + ifcMaterial = self.f.createIfcMaterial(material['name'], None, material['category']) + + mechProps = [] + if 'youngModulus' in material['mechProps']: + youngModulus = self.f.createIfcPropertySingleValue('YoungModulus', None, self.f.createIfcModulusOfElasticityMeasure(material['mechProps']['youngModulus'])) + mechProps.append(youngModulus) + if 'shearModulus' in material['mechProps']: + shearModulus = self.f.createIfcPropertySingleValue('ShearModulus', None, self.f.createIfcModulusOfElasticityMeasure(material['mechProps']['shearModulus'])) + mechProps.append(shearModulus) + if 'poissonRatio' in material['mechProps']: + poissonRatio = self.f.createIfcPropertySingleValue('PoissonRatio', None, self.f.createIfcPositiveRatioMeasure(material['mechProps']['poissonRatio'])) + mechProps.append(poissonRatio) + if mechProps: + self.f.createIfcMaterialProperties('Pset_MaterialMechanical', material['name'], tuple(mechProps), ifcMaterial) + + commonProps = [] + if 'massDensity' in material['commonProps']: + massDensity = self.f.createIfcPropertySingleValue('MassDensity', None, self.f.createIfcMassDensityMeasure(material['commonProps']['massDensity'])) + commonProps.append(massDensity) + if commonProps: + self.f.createIfcMaterialProperties('Pset_MaterialCommon', material['name'], tuple(commonProps), ifcMaterial) + + return ifcMaterial + + def create_profile(self, profile): + if profile['profileShape'] == 'rectangular': + ifcProfile = self.f.createIfcRectangleProfileDef(profile['profileType'], profile['profileName'], None, profile['xDim'], profile['yDim']) + + if profile['profileShape'] == 'iSymmetrical': + ifcProfile = self.f.createIfcIShapeProfileDef( + profile['profileType'], profile['profileName'], None, + profile['commonProps']['overallWidth'], + profile['commonProps']['overallDepth'], + profile['commonProps']['webThickness'], + profile['commonProps']['flangeThickness'], + profile['commonProps']['filletRadius'] + ) + + mechProps = [] + if 'massPerLength' in profile['mechProps']: + massPerLength = self.f.createIfcPropertySingleValue('MassPerLength', None, self.f.createIfcMassPerLengthMeasure(profile['mechProps']['massPerLength'])) + mechProps.append(massPerLength) + if 'crossSectionArea' in profile['mechProps']: + crossSectionArea = self.f.createIfcPropertySingleValue('CrossSectionArea', None, self.f.createIfcAreaMeasure(profile['mechProps']['crossSectionArea'])) + mechProps.append(crossSectionArea) + if 'momentOfInertiaY' in profile['mechProps']: + momentOfInertiaY = self.f.createIfcPropertySingleValue('MomentOfInertiaY', None, self.f.createIfcMomentOfInertiaMeasure(profile['mechProps']['momentOfInertiaY'])) + mechProps.append(momentOfInertiaY) + if 'momentOfInertiaZ' in profile['mechProps']: + momentOfInertiaZ = self.f.createIfcPropertySingleValue('MomentOfInertiaZ', None, self.f.createIfcMomentOfInertiaMeasure(profile['mechProps']['momentOfInertiaZ'])) + mechProps.append(momentOfInertiaZ) + if 'torsionalConstantX' in profile['mechProps']: + torsionalConstantX = self.f.createIfcPropertySingleValue('TorsionalConstantX', None, self.f.createIfcMomentOfInertiaMeasure(profile['mechProps']['torsionalConstantX'])) + mechProps.append(torsionalConstantX) + if mechProps: + self.f.createIfcProfileProperties('Pset_ProfileMechanical', profile['profileName'], tuple(mechProps), ifcProfile) + + return ifcProfile + + def create_geometry(self, object): + if object['geometryType'] == 'point': + point = self.f.createIfcCartesianPoint(tuple(object['geometry'])) + vertex = self.f.createIfcVertexPoint(point) + vertexTopologyRep = self.f.createIfcTopologyRepresentation(self.reps['reference'], 'Reference', 'Vertex', (vertex,)) + vertexProdDefShape = self.f.createIfcProductDefinitionShape(None, None, (vertexTopologyRep,)) + + return vertexProdDefShape + + if object['geometryType'] == 'line': + startPoint = self.f.createIfcCartesianPoint(tuple(object['geometry'][0])) + startVertex = self.f.createIfcVertexPoint(startPoint) + endPoint = self.f.createIfcCartesianPoint(tuple(object['geometry'][1])) + endVertex = self.f.createIfcVertexPoint(endPoint) + edge = self.f.createIfcEdge(startVertex, endVertex) + edgeTopologyRep = self.f.createIfcTopologyRepresentation(self.reps['reference'], 'Reference', 'Edge', (edge,)) + edgeProdDefShape = self.f.createIfcProductDefinitionShape(None, None, (edgeTopologyRep,)) + + return edgeProdDefShape + + if object['geometryType'] == 'surface': + verts = [None for _ in range(len(object['geometry']))] + for i,p in enumerate(object['geometry']): + point = self.f.createIfcCartesianPoint(tuple(p)) + verts[i] = self.f.createIfcVertexPoint(point) + + orientedEdges = [None for _ in range(len(object['geometry']))] + for i,v in enumerate(verts): + v2Index = (i + 1) if i < len(verts) - 1 else 0 + edge = self.f.createIfcEdge(v, verts[v2Index]) + orientedEdges[i] = self.f.createIfcOrientedEdge(None, None, edge, True) + + edgeLoop = self.f.createIfcEdgeLoop(tuple(orientedEdges)) + localAxes = self.create_orientation(object['orientation']) + plane = self.f.createIfcPlane(localAxes) + faceBound = self.f.createIfcFaceBound(edgeLoop, True) + face = self.f.createIfcFaceSurface((faceBound,), plane, True) + faceTopologyRep = self.f.createIfcTopologyRepresentation(self.reps['reference'], 'Reference', 'Face', (face,)) + faceProdDefShape = self.f.createIfcProductDefinitionShape(None, None, (faceTopologyRep,)) + + return faceProdDefShape + +if __name__ == '__main__': + inputFilename = '' # json file to read + outputFilename = '' # ifc file to write + + ca2ifc = CA2IFC(inputFilename, outputFilename) + ca2ifc.convert()