From e071c7812f00966757bbcb6a93064cf88c82cb6b Mon Sep 17 00:00:00 2001 From: Jesusbill Date: Wed, 27 May 2020 05:33:36 +0200 Subject: [PATCH] major update, refactoring to consider generalization of connections --- src/ifc2ca/README.md | 42 +- src/ifc2ca/changelog.md | 24 + src/ifc2ca/ifc2ca.py | 227 +++++++-- src/ifc2ca/scriptCodeAster.py | 916 ++++++++++++++++++++-------------- src/ifc2ca/scriptSalome.py | 231 +++++---- 5 files changed, 882 insertions(+), 558 deletions(-) create mode 100644 src/ifc2ca/changelog.md diff --git a/src/ifc2ca/README.md b/src/ifc2ca/README.md index 58cf6ffa28..e11081223f 100644 --- a/src/ifc2ca/README.md +++ b/src/ifc2ca/README.md @@ -7,47 +7,11 @@ Files and scripts for the use of [`Code_Aster`](https://code-aster.org) in IFC-d - [`scriptSalome.py`](scriptSalome.py): a python script to run in the [`Salome-Meca`](https://www.code-aster.org/spip.php?article303) environment. Creates the geometry and the mesh of the structure - [`scriptCodeAster.py`](scriptCodeAster.py): a python script to create the input file (`.comm`) for Code_Aster -#### Examples +#### Analysis Models -All examples are contained in a separate repository within the IfcOpenShell organization (Work In Progress). - -- `cantilever_01` (model created by Dion Moult) -- `beam_01` (model created by Tandeep Singh) -- `portal_01` (model created by buildingSmart) -- `building-frame_01` (model created by Tandeep Singh) -- `building_01`] (model created by Tandeep Singh) - -All example folders contain the following files: - -__Input:__ -- `{example_name}.ifc`: ifc file of the example -- `{example_name}.json`: json data file of the example -- `bldMesh.med`: mesh file exported from Salome_Meca after executing `scriptSalome` -- `CA_input_00.comm`: command file generated from `scriptCodeAster` - -__Output:__ -- `result.mess`: message log file of the interpreted commands in Code_Aster -- `result.rmed`: result file on the mesh of the structure to visualize in Salome_Meca +A number of analysis models with all the relative input/output and script files are provided in [this repository](https://github.com/IfcOpenShell/analysis-models) within the IfcOpenShell Organization --- ### Current Status -_As of 22/03/20:_ -- Added beam and building examples -- Added support for I sections and calculation of section properties if not provided (assuming fillet radius is zero) -- Added support for surface members (shells) -- Refactored the `ifc2ca.py` script -- Added support for rigid links -- Added advanced meshing script to correctly simulate connections and independent meshing of structural elements (no common nodes) - -_As of 26/02/20:_ -- Added portal example -- Changed `section` to `profile` and added I profile -- Modified material and profile schema with mechanical and common properties -- Added geometry identification for point connection from representation -- Added connections along with supports based on the number of elements a connection is applied to -- Applied conditions with elastic stiffness values is still not implemented. Only True/False values are accepted. - -_As of 16/01/20:_ -- Only line geometries for structural elements and point geometries for supports are considered -- The structure is analysed for gravity loads with a single linear static analysis +Read [Change Log](changelog.md) diff --git a/src/ifc2ca/changelog.md b/src/ifc2ca/changelog.md new file mode 100644 index 0000000000..56d502b2f7 --- /dev/null +++ b/src/ifc2ca/changelog.md @@ -0,0 +1,24 @@ +## A change log of the ifc2ca files + +##### 27/05/20 +- Add orientation for point, curve and surface geometries +- Calculate final geometry and orientation based on object placement transformation +- Add warnings in the json file to show any corrections considered while parsing the ifc file from ETABS with a model with rigid links +- Reduce file size by adding material and profile db in the json file and referencing them in the element objects +- Implement internal releases for curve-to-point connections with any arbitrary orientation + +##### 22/03/20 +- Calculation of section properties for I symmetric profile if not provided (assuming fillet radius is zero) +- Added geometry of planar structural surface members +- Refactored the ifc2ca.py script +- Added support for rigid links +- Added advanced meshing script to correctly simulate connections and independent meshing of structural elements (no common nodes) + +##### 26/02/20 +- Changed section to profile and added I profile +- Modified material and profile schema with mechanical and common properties +- Added geometry identification for point connection from representation +- Added connections along with supports based on the number of elements a connection is applied to + +##### 09/02/20 +- First implementation for straight structural curve elements and point connections diff --git a/src/ifc2ca/ifc2ca.py b/src/ifc2ca/ifc2ca.py index f981a6bdd3..b2a5ee2644 100644 --- a/src/ifc2ca/ifc2ca.py +++ b/src/ifc2ca/ifc2ca.py @@ -7,20 +7,49 @@ class IFC2CA: self.filename = filename self.file = None self.result = {} + self.warnings = [] def convert(self): self.file = ifcopenshell.open(self.filename) for model in self.file.by_type('IfcStructuralAnalysisModel'): + elements = self.get_structural_items(model, item_type='IfcStructuralMember') + connections = self.get_structural_items(model, item_type='IfcStructuralConnection') + + materialdb = [] + materials = list(dict.fromkeys([e['material'] for e in elements])) + for mat in materials: + id = int(mat.split('|')[1]) + material = self.get_material_properties(self.file.by_id(id)) + material['relatedElements'] = [e['ifcName'] for e in elements if 'material' in e and e['material'] == mat] + materialdb.append(material) + + profiledb = [] + profiles = list(dict.fromkeys([e['profile'] for e in elements if 'profile' in e])) + for prof in profiles: + id = int(prof.split('|')[1]) + profile = self.get_profile_properties(self.file.by_id(id)) + profile['relatedElements'] = [e['ifcName'] for e in elements if 'profile' in e and e['profile'] == prof] + profiledb.append(profile) + self.result = { 'ifcName': model.is_a() + '|' + str(model.id()), 'name': model.Name, 'id': model.GlobalId, - 'elements': self.get_structural_items(model, item_type='IfcStructuralMember'), - 'connections': self.get_structural_items(model, item_type='IfcStructuralConnection') + 'elements': elements, + 'connections': connections, + 'db': { + 'materials': materialdb, + 'profiles': profiledb + }, + 'warnings': self.warnings } - print('Number of elements: ', len(self.result['elements'])) - print('Number of connections: ', len(self.result['connections'])) + print('Model %s converted' % model.Name) + print('Number of elements: ', len(elements)) + print('Number of connections: ', len(connections)) + print('Number of materials: ', len(materialdb)) + print('Number of profiles: ', len(profiledb)) + print('') break @@ -36,6 +65,8 @@ class IFC2CA: return items def get_item_data(self, item): + transformation = self.get_transformation(item.ObjectPlacement) + if item.is_a('IfcStructuralCurveMember'): representation = self.get_representation(item, 'Edge') material_profile = self.get_material_profile(item) @@ -43,7 +74,30 @@ class IFC2CA: print(representation, material_profile) return + material = material_profile.Material + profile = material_profile.Profile geometry = self.get_geometry(representation) + orientation = self.get_1D_orientation(geometry, item.Axis) + connections = self.get_connection_data(item.ConnectedBy) + for conn in connections: + if not conn['orientation']: + conn['orientation'] = orientation + # --> Correct pointOnElement for eccentricity connection for ETABS files + length = np.linalg.norm(np.array(geometry[1]) - np.array(geometry[0])) + for c in connections: + if c['eccentricity']: + if np.linalg.norm(np.array(c['eccentricity']['pointOnElement'])) > length: + # print('Eccentricity in %s corrected' % item.is_a() + '|' + str(item.id())) + self.warnings.append('Eccentricity in %s corrected' % (item.is_a() + '|' + str(item.id()))) + c['eccentricity']['pointOnElement'][0] = length + # End <-- + if transformation: + geometry = self.transform_vectors(geometry, transformation) + orientation = self.transform_vectors(orientation, transformation, include_translation=False) + for c in connections: + c['orientation'] = self.transform_vectors(c['orientation'], transformation, include_translation=False) + if c['eccentricity']: + c['eccentricity']['vector'] = self.transform_vectors(c['eccentricity']['vector'], transformation, include_translation=False) return { 'ifcName': item.is_a() + '|' + str(item.id()), @@ -52,10 +106,10 @@ class IFC2CA: 'geometryType': 'line', 'predefinedType': item.PredefinedType, 'geometry': geometry, - 'orientation': self.get_1D_orientation(geometry, item.Axis), - 'material': self.get_material_properties(material_profile.Material), - 'profile': self.get_profile_properties(material_profile.Profile), - 'connections': self.get_connection_data(item.ConnectedBy) + 'orientation': orientation, + 'material': material.is_a() + '|' + str(material.id()), + 'profile': profile.is_a() + '|' + str(profile.id()), + 'connections': connections } elif item.is_a('IfcStructuralSurfaceMember'): @@ -65,6 +119,18 @@ class IFC2CA: print(representation) return + geometry = self.get_geometry(representation) + orientation = self.get_2D_orientation(representation) + connections = self.get_connection_data(item.ConnectedBy) + for conn in connections: + if not conn['orientation']: + conn['orientation'] = orientation + if transformation: + geometry = self.transform_vectors(geometry, transformation) + orientation = self.transform_vectors(orientation, transformation, include_translation=False) + for c in connections: + c['orientation'] = self.transform_vectors(c['orientation'], transformation, include_translation=False) + return { 'ifcName': item.is_a() + '|' + str(item.id()), 'name': item.Name, @@ -72,9 +138,10 @@ class IFC2CA: 'geometryType': 'surface', 'predefinedType': item.PredefinedType, 'thickness': item.Thickness, - 'geometry': self.get_geometry(representation), - 'material': self.get_material_properties(material), - 'connections': self.get_connection_data(item.ConnectedBy) + 'geometry': geometry, + 'orientation': orientation, + 'material': material.is_a() + '|' + str(material.id()), + 'connections': connections } elif item.is_a('IfcStructuralPointConnection'): @@ -83,17 +150,60 @@ class IFC2CA: print(representation) return + geometry = self.get_geometry(representation) + orientation = self.get_0D_orientation(item.ConditionCoordinateSystem) + if not orientation: + orientation = np.eye(3).tolist() + if transformation: + geometry = self.transform_vectors(geometry, transformation) + orientation = self.transform_vectors(orientation, transformation, include_translation=False) + return { 'ifcName': item.is_a() + '|' + str(item.id()), 'name': item.Name, 'id': item.GlobalId, 'geometryType': 'point', - 'geometry': self.get_geometry(representation), - 'orientation': self.get_0D_orientation(item.ConditionCoordinateSystem), + 'geometry': geometry, + 'orientation': orientation, 'appliedCondition': self.get_connection_input(item), - 'relatedElements': self.get_connection_data(item.ConnectsStructuralMembers) + 'relatedElements': [con.is_a() + '|' + str(con.id()) for con in item.ConnectsStructuralMembers] } + def get_transformation(self, placement): + if not placement: + return None + if placement.is_a('IfcLocalPlacement'): + if placement.PlacementRelTo: + print('Warning! Object Placement with PlacementRelTo attribute is not supported and will be neglected') + axes = placement.RelativePlacement + location = np.array(self.get_coordinate(axes.Location)) + if axes.Axis and axes.RefDirection: + xAxis = np.array(axes.RefDirection.DirectionRatios) # this can be not accurate (in the xz plane) + zAxis = np.array(axes.Axis.DirectionRatios) + zAxis /= np.linalg.norm(zAxis) + yAxis = np.cross(zAxis, xAxis) + yAxis /= np.linalg.norm(yAxis) + xAxis = np.cross(yAxis, zAxis) + xAxis /= np.linalg.norm(xAxis) + else: + if np.allclose(location, np.array([0., 0., 0.])): + return None + xAxis = np.array([1., 0., 0.]) + yAxis = np.array([0., 1., 0.]) + zAxis = np.array([0., 0., 1.]) + if (np.allclose(location, np.array([0., 0., 0.])) and + np.allclose(xAxis, np.array([1., 0., 0.])) and + np.allclose(yAxis, np.array([0., 1., 0.])) and + np.allclose(zAxis, np.array([0., 0., 1.]))): + return None + return { + 'location': location, + 'rotationMatrix': np.array([xAxis, yAxis, zAxis]).transpose() + } + else: + print('Warning! Object Placement is of type %s, which is not supported. Default considered' % placement.is_a()) + return None + def get_representation(self, element, rep_type): if not element.Representation: return None @@ -140,41 +250,58 @@ class IFC2CA: def get_coordinate(self, point): if point.is_a('IfcCartesianPoint'): - return point.Coordinates + return list(point.Coordinates) def get_0D_orientation(self, axes): if axes and axes.Axis and axes.RefDirection: - xAxis = np.array(axes.RefDirection.DirectionRatios) # this can be not accurate (in the xz plane) + xAxis = np.array(axes.RefDirection.DirectionRatios) # this can be not strictly perpendicular (in the xz plane) zAxis = np.array(axes.Axis.DirectionRatios) + zAxis /= np.linalg.norm(zAxis) yAxis = np.cross(zAxis, xAxis) yAxis /= np.linalg.norm(yAxis) xAxis = np.cross(yAxis, zAxis) xAxis /= np.linalg.norm(xAxis) - # print('0D:', xAxis, yAxis, zAxis) - value = xAxis.tolist() - value.extend(yAxis.tolist()) - return { - 'type': 'xyPlane', - 'value': value - } + + return [xAxis.tolist(), yAxis.tolist(), zAxis.tolist()] + else: # return None and copy the elements orientation + return None def get_1D_orientation(self, geometry, zAxis): - if zAxis: - xAxis = np.array(geometry[1]) - np.array(geometry[0]) - zAxis = np.array(zAxis.DirectionRatios) - yAxis = np.cross(zAxis, xAxis) - yAxis /= np.linalg.norm(yAxis) - # print('1D:', xAxis, yAxis, zAxis) - return { - 'type': 'yAxis', - 'value': yAxis.tolist() - } - else: - print('Warning! Orientation for curve member missing. Default considered') - return { - 'type': 'rotationAngle', - 'value': 0 - } + xAxis = np.array(geometry[1]) - np.array(geometry[0]) + xAxis /= np.linalg.norm(xAxis) + zAxis = np.array(zAxis.DirectionRatios) # this can be not strictly perpendicular (in the xz plane) + yAxis = np.cross(zAxis, xAxis) + yAxis /= np.linalg.norm(yAxis) + zAxis = np.cross(xAxis, yAxis) + zAxis /= np.linalg.norm(zAxis) + + return [xAxis.tolist(), yAxis.tolist(), zAxis.tolist()] + + def get_2D_orientation(self, representation): + item = representation.Items[0] + if item.is_a('IfcFaceSurface'): + item.SameSense + axes = item.FaceSurface.Position + orientation = self.get_0D_orientation(axes) + if not item.SameSense: + orientation = [[-v for v in vec] for vec in orientation] + return orientation + + def transform_vectors(self, geometry, trsf, include_translation=True): + if not any(isinstance(el, list) for el in geometry): # single point which contains no list + geometry = [geometry] + globalGeometry = [] + + for p in geometry: + gp = trsf['rotationMatrix'].dot(np.array(p)) + if include_translation: + gp += trsf['location'] + globalGeometry.append(gp.tolist()) + + if len(globalGeometry) == 1: # single point + globalGeometry = globalGeometry[0] + + return globalGeometry def get_material_profile(self, element): if not element.HasAssociations: @@ -270,12 +397,13 @@ class IFC2CA: 'orientation': self.get_0D_orientation(rel.ConditionCoordinateSystem), 'appliedCondition': self.get_connection_input(rel), 'eccentricity': None if not rel.is_a('IfcRelConnectsWithEccentricity') else { - 'inX': 0.0 if not rel.ConnectionConstraint.EccentricityInX else rel.ConnectionConstraint.EccentricityInX, - 'inY': 0.0 if not rel.ConnectionConstraint.EccentricityInY else rel.ConnectionConstraint.EccentricityInY, - 'inZ': 0.0 if not rel.ConnectionConstraint.EccentricityInZ else rel.ConnectionConstraint.EccentricityInZ, + 'vector': [ + 0.0 if not rel.ConnectionConstraint.EccentricityInX else rel.ConnectionConstraint.EccentricityInX, + 0.0 if not rel.ConnectionConstraint.EccentricityInY else rel.ConnectionConstraint.EccentricityInY, + 0.0 if not rel.ConnectionConstraint.EccentricityInZ else rel.ConnectionConstraint.EccentricityInZ + ], 'pointOnElement': self.get_coordinate(rel.ConnectionConstraint.PointOnRelatingElement) } - # 'geometryPointIndex': None } for rel in itemList] def get_connection_input(self, connection): @@ -310,7 +438,12 @@ class IFC2CA: } if __name__ == '__main__': - IFC_FILENAME = '' - ifc2ca = IFC2CA(IFC_FILENAME) - ifc2ca.convert() - print(json.dumps(ifc2ca.result, indent=4)) + fileNames = ['cantilever_01', 'portal_01']; + files = fileNames + + for fileName in files: + BASE_PATH = '/home/jesusbill/Dev-Projects/github.com/IfcOpenShell/analysis-models/ifcFiles/' + ifc2ca = IFC2CA(BASE_PATH + fileName + '.ifc') + ifc2ca.convert() + with open(BASE_PATH + fileName + '.json', 'w') as f: + f.write(json.dumps(ifc2ca.result, indent = 4)) diff --git a/src/ifc2ca/scriptCodeAster.py b/src/ifc2ca/scriptCodeAster.py index 373224f4b5..5307d3ba68 100644 --- a/src/ifc2ca/scriptCodeAster.py +++ b/src/ifc2ca/scriptCodeAster.py @@ -1,68 +1,91 @@ import json -import codecs +import numpy as np -def getGroupName(name): - info = name.split('|') - sortName = ''.join(c for c in info[0] if c.isupper()) - return str(sortName + '_' + info[1]) +class COMMANDFILE: + def __init__(self, dataFilename, asterFilename): + self.dataFilename = dataFilename + self.asterFilename = asterFilename + self.create() -def createCommFile(FILENAME, FILENAMEASTER): + def getGroupName(self, name): + info = name.split('|') + sortName = ''.join(c for c in info[0] if c.isupper()) + return str(sortName + '_' + info[1]) - AccelOfGravity = 9.806 # m/sec^2 + def create(self): - # Read data from input file - with open(FILENAME) as dataFile: - data = json.load(dataFile) + AccelOfGravity = 9.806 # m/sec^2 - elements = data['elements'] - connections = data['connections'] + # Read data from input file + with open(self.dataFilename) as dataFile: + data = json.load(dataFile) - edgeGroupNames = tuple([getGroupName(str(el['ifcName'])) for el in elements if el['geometryType'] == 'line']) - faceGroupNames = tuple([getGroupName(str(el['ifcName'])) for el in elements if el['geometryType'] == 'surface']) + elements = data['elements'] + connections = data['connections'] + # --> Delete this reference data and repopulate it with the objects + # while going through elements + for conn in connections: + conn['relatedElements'] = [] + self.calculateRestraints(conn) + for el in elements: + for rel in el['connections']: + conn = [c for c in connections if c['ifcName'] == rel['relatedConnection']][0] + if conn['geometryType'] == 'point': + rel['groupName'] = self.getGroupName(rel['relatingElement']) + '_0DC_' + self.getGroupName(rel['relatedConnection']) + self.calculateConstraints(rel) + conn['relatedElements'].append(rel) + # End <-- - unifiedConnection = False - rigidLinkGroupNames = [] - for conn in connections: - conn['relatedGroupNames'] = tuple([getGroupName(str(rel['relatingElement'])) + '_0D_to_' + getGroupName(str(conn['ifcName'])) for rel in conn['relatedElements']]) - if not conn['appliedCondition'] and len(conn['relatedGroupNames']) == 1: - conn['appliedCondition'] = { - 'dx': True, - 'dy': True, - 'dz': True - } - if len(conn['relatedGroupNames']) > 1: - unifiedConnection = True - rigidLinkGroupNames.extend([getGroupName(str(rel['relatingElement'])) + '_1D_to_' + getGroupName(str(conn['ifcName'])) for rel in conn['relatedElements'] if rel['eccentricity']]) - rigidLinkGroupNames = tuple(rigidLinkGroupNames) + materials = data['db']['materials'] + profiles = data['db']['profiles'] - # Define file to write command file for code_aster - f = open(FILENAMEASTER, 'w') + edgeGroupNames = tuple([self.getGroupName(el['ifcName']) for el in elements if el['geometryType'] == 'line']) + faceGroupNames = tuple([self.getGroupName(el['ifcName']) for el in elements if el['geometryType'] == 'surface']) + point0DGroupNames = tuple([self.getGroupName(el['ifcName']) for el in connections if el['geometryType'] == 'point']) - f.write('# Command file generated for ifcOpenShell/BlenderBim\n') - f.write('# Aether Engineering - www.aethereng.com\n') - f.write('\n') + unifiedConnection = False + rigidLinkGroupNames = [] + # for conn in connections: + # conn['relatedGroupNames'] = tuple([self.getGroupName(rel['relatingElement']) + '_0DC_' + self.getGroupName(conn['ifcName']) for rel in conn['relatedElements']]) + # if not conn['appliedCondition'] and len(conn['relatedGroupNames']) == 1: + # conn['appliedCondition'] = { + # 'dx': True, + # 'dy': True, + # 'dz': True + # } + # if len(conn['relatedGroupNames']) > 1: + # unifiedConnection = True + # rigidLinkGroupNames.extend([self.getGroupName(rel['relatingElement']) + '_1DC_' + self.getGroupName(conn['ifcName']) for rel in conn['relatedElements'] if rel['eccentricity']]) + # rigidLinkGroupNames = tuple(rigidLinkGroupNames) - f.write('# Linear Static Analysis With Self-Weight\n') + # Define file to write command file for code_aster + f = open(self.asterFilename, 'w') - f.write( + f.write('# Command file generated by IfcOpenShell/ifc2ca scripts\n') + f.write('\n') + + f.write('# Linear Static Analysis With Self-Weight\n') + + f.write( ''' # STEP: INITIALIZE STUDY DEBUT( PAR_LOT = 'NON' ) ''' - ) + ) - f.write( + f.write( ''' # STEP: READ MED FILE mesh = LIRE_MAILLAGE( - FORMAT = 'MED' + FORMAT = 'MED', + UNITE = 20 ) ''' - ) + ) - f.write( + f.write( ''' # STEP: DEFINE MODEL model = AFFE_MODELE( @@ -73,65 +96,80 @@ model = AFFE_MODELE( PHENOMENE = 'MECANIQUE', MODELISATION = '3D' ),''' - ) + ) - if faceGroupNames: - template = \ + if faceGroupNames: + template = \ ''' _F( - GROUP_MA = {group_names}, + GROUP_MA = {groupNames}, PHENOMENE = 'MECANIQUE', MODELISATION = 'DKT' ),''' - context = { - 'group_names': faceGroupNames - } + context = { + 'groupNames': faceGroupNames + } - f.write(template.format(**context)) + f.write(template.format(**context)) - if edgeGroupNames: - template = \ + if edgeGroupNames: + template = \ ''' _F( - GROUP_MA = {group_names}, + GROUP_MA = {groupNames}, PHENOMENE = 'MECANIQUE', MODELISATION = 'POU_D_E' ),''' - context = { - 'group_names': edgeGroupNames - } + context = { + 'groupNames': edgeGroupNames + } - f.write(template.format(**context)) + f.write(template.format(**context)) - if rigidLinkGroupNames: - template = \ + if point0DGroupNames: + template = \ ''' _F( - GROUP_MA = {group_names}, + GROUP_MA = {groupNames}, + PHENOMENE = 'MECANIQUE', + MODELISATION = 'DIS_TR' + ),''' + + context = { + 'groupNames': point0DGroupNames + } + + f.write(template.format(**context)) + + if rigidLinkGroupNames: + template = \ + ''' + _F( + GROUP_MA = {groupNames}, PHENOMENE = 'MECANIQUE', MODELISATION = 'POU_D_E' ),''' - context = { - 'group_names': rigidLinkGroupNames - } + context = { + 'groupNames': rigidLinkGroupNames + } - f.write(template.format(**context)) + f.write(template.format(**context)) - f.write( + f.write( ''' ) )\n ''' - ) + ) - f.write('# STEP: DEFINE MATERIALS') + f.write('# STEP: DEFINE MATERIALS') - for i,el in enumerate(elements): - template = \ + for i,material in enumerate(materials): + template = \ ''' {matNameID} = DEFI_MATERIAU( ELAS = _F( @@ -141,305 +179,366 @@ model = AFFE_MODELE( ) ) ''' - if 'poissonRatio' in el['material']['mechProps']: - poissonRatio = el['material']['mechProps']['poissonRatio'] - else: - if 'shearModulus' in el['material']['mechProps']: - poissonRatio = (el['material']['mechProps']['youngModulus'] / 2.0 / el['material']['mechProps']['shearModulus']) - 1 + if 'poissonRatio' in material['mechProps']: + poissonRatio = material['mechProps']['poissonRatio'] else: - poissonRation = 0 + if 'shearModulus' in material['mechProps']: + poissonRatio = (material['mechProps']['youngModulus'] / 2.0 / material['mechProps']['shearModulus']) - 1 + else: + poissonRation = 0 - context = { - 'matNameID': 'matF'+ '_%s' % i, - 'youngModulus': float(el['material']['mechProps']['youngModulus']), - 'poissonRatio': float(poissonRatio), - 'massDensity': float(el['material']['commonProps']['massDensity']) - } + context = { + 'matNameID': 'mat'+ '_%s' % i, + 'youngModulus': float(material['mechProps']['youngModulus']), + 'poissonRatio': float(poissonRatio), + 'massDensity': float(material['commonProps']['massDensity']) + } - f.write(template.format(**context)) + f.write(template.format(**context)) - f.write( + f.write( ''' material = AFFE_MATERIAU( MAILLAGE = mesh, AFFE = (''' - ) + ) - for i,el in enumerate(elements): - template = \ + for i,material in enumerate(materials): + template = \ ''' _F( - GROUP_MA = '{group_name}', + GROUP_MA = {groupNames}, MATER = {matNameID}, ),''' - context = { - 'group_name': getGroupName(str(el['ifcName'])), - 'matNameID': 'matF'+ '_%s' % i - } + context = { + 'groupNames': tuple([self.getGroupName(rel) for rel in material['relatedElements']]), + 'matNameID': 'mat'+ '_%s' % i + } - f.write(template.format(**context)) + f.write(template.format(**context)) - if rigidLinkGroupNames: - template = \ + if rigidLinkGroupNames: + template = \ ''' _F( - GROUP_MA = {group_names}, + GROUP_MA = {groupNames}, MATER = {matNameID}, ),''' - context = { - 'group_names': rigidLinkGroupNames, - 'matNameID': 'matF_0' - } + context = { + 'groupNames': rigidLinkGroupNames, + 'matNameID': 'mat_0' + } - f.write(template.format(**context)) + f.write(template.format(**context)) - f.write( + f.write( ''' ) ) ''' - ) + ) - f.write( + f.write( ''' # STEP: DEFINE ELEMENTS element = AFFE_CARA_ELEM( MODELE = model, POUTRE = (''' - ) + ) - for el in [el for el in elements if el['geometryType'] == 'line']: - if el['profile']['profileShape'] == 'rectangular': - template = \ + for profile in profiles: + if profile['profileShape'] == 'rectangular' and profile['profileType'] == 'AREA': + template = \ ''' _F( - GROUP_MA = '{group_name}', + GROUP_MA = {groupNames}, SECTION = 'RECTANGLE', CARA = ('HY', 'HZ'), VALE = {profileDimensions} ),''' - context = { - 'group_name': getGroupName(str(el['ifcName'])), - 'profileDimensions': (el['profile']['xDim'], el['profile']['yDim']) - } + context = { + 'groupNames': tuple([self.getGroupName(rel) for rel in profile['relatedElements']]), + 'profileDimensions': (profile['xDim'], profile['yDim']) + } - f.write(template.format(**context)) + f.write(template.format(**context)) - elif el['profile']['profileShape'] == 'iSymmetrical': - template = \ + elif profile['profileShape'] == 'iSymmetrical' and profile['profileType'] == 'AREA': + template = \ ''' _F( - GROUP_MA = '{group_name}', + GROUP_MA = {groupNames}, SECTION = 'GENERALE', CARA = ('A', 'IY', 'IZ', 'JX'), VALE = {profileProperties} ),''' - context = { - 'group_name': getGroupName(str(el['ifcName'])), - 'profileProperties': ( - el['profile']['mechProps']['crossSectionArea'], - el['profile']['mechProps']['momentOfInertiaY'], - el['profile']['mechProps']['momentOfInertiaZ'], - el['profile']['mechProps']['torsionalConstantX'] - ) - } + context = { + 'groupNames': tuple([self.getGroupName(rel) for rel in profile['relatedElements']]), + 'profileProperties': ( + profile['mechProps']['crossSectionArea'], + profile['mechProps']['momentOfInertiaY'], + profile['mechProps']['momentOfInertiaZ'], + profile['mechProps']['torsionalConstantX'] + ) + } - f.write(template.format(**context)) + f.write(template.format(**context)) - if rigidLinkGroupNames: - template = \ + if rigidLinkGroupNames: + template = \ ''' _F( - GROUP_MA = {group_names}, + GROUP_MA = {groupNames}, SECTION = 'RECTANGLE', CARA = ('HY', 'HZ'), VALE = {profileDimensions} ),''' - context = { - 'group_names': rigidLinkGroupNames, - 'profileDimensions': (1, 1) - } + context = { + 'groupNames': rigidLinkGroupNames, + 'profileDimensions': (1, 1) + } - f.write(template.format(**context)) + f.write(template.format(**context)) - f.write( + f.write( ''' ), COQUE = (''' - ) + ) - for el in [el for el in elements if el['geometryType'] == 'surface']: + for el in [el for el in elements if el['geometryType'] == 'surface']: - template = \ + template = \ ''' _F( - GROUP_MA = '{group_name}', + GROUP_MA = '{groupName}', EPAIS = {thickness}, - VECTEUR = {orientationVector} + VECTEUR = {localAxisX} ),''' - context = { - 'group_name': getGroupName(str(el['ifcName'])), - 'thickness': el['thickness'], - 'orientationVector': ( - el['geometry'][1][0] - el['geometry'][0][0], - el['geometry'][1][1] - el['geometry'][0][1], - el['geometry'][1][2] - el['geometry'][0][2] + context = { + 'groupName': self.getGroupName(el['ifcName']), + 'thickness': el['thickness'], + 'localAxisX': tuple(el['orientation'][0]) + } + + f.write(template.format(**context)) + + f.write( +''' + ),''' + ) + f.write( +''' + DISCRET = (''' + ) + + for conn in [conn for conn in connections if conn['geometryType'] == 'point']: + + template = \ + ''' + _F( + GROUP_MA = '{groupName}', + CARA = 'K_TR_D_N', + VALE = (0.0, 0.0, 0.0, 0.0, 0.0, 0.0), + REPERE = 'LOCAL' + ),''' + + context = { + 'groupName': self.getGroupName(conn['ifcName']) + } + + f.write(template.format(**context)) + + f.write( +''' + ),''' + ) + + f.write( +''' + ORIENTATION = (''' + ) + + for el in [el for el in elements if el['geometryType'] == 'line']: + + template = \ + ''' + _F( + GROUP_MA = '{groupName}', + CARA = 'VECT_Y', + VALE = {localAxisY} + ),''' + + context = { + 'groupName': self.getGroupName(el['ifcName']), + 'localAxisY': tuple(el['orientation'][1]) + } + + f.write(template.format(**context)) + + for conn in [conn for conn in connections if conn['geometryType'] == 'point']: + + template = \ + ''' + _F( + GROUP_MA = '{groupName}', + CARA = 'VECT_X_Y', + VALE = {localAxesXY} + ),''' + + context = { + 'groupName': self.getGroupName(conn['ifcName']), + 'localAxesXY': tuple(conn['orientation'][0] + conn['orientation'][1]) + } + + f.write(template.format(**context)) + + f.write( +''' + ),''' + ) + + f.write( +''' +)\n +''' + ) + + + f.write('# STEP: DEFINE SUPPORTS AND CONSTRAINTS') + + f.write( +''' +liaisons = AFFE_CHAR_MECA( + MODELE = model, + LIAISON_DDL = (''' + ) + + for conn in connections: + if conn['appliedCondition']: + for i in range(len(conn['liaisons']['coeffs'])): + template = \ + ''' + _F( + GROUP_NO = {groupNames}, + DDL = {dofs}, + COEF_MULT = {coeffs}, + COEF_IMPO = 0.0 + ),''' + + context = { + 'groupNames': conn['liaisons']['groupNames'], + 'dofs': conn['liaisons']['dofs'][i], + 'coeffs': conn['liaisons']['coeffs'][i] + } + + f.write(template.format(**context)) + + for rel in conn['relatedElements']: + for i in range(len(rel['liaisons']['coeffs'])): + template = \ + ''' + _F( + GROUP_NO = {groupNames}, + DDL = {dofs}, + COEF_MULT = {coeffs}, + COEF_IMPO = 0.0 + ),''' + + context = { + 'groupNames': rel['liaisons']['groupNames'], + 'dofs': rel['liaisons']['dofs'][i], + 'coeffs': rel['liaisons']['coeffs'][i] + } + + f.write(template.format(**context)) + + f.write( + ''' + ),''' + ) + + if unifiedConnection: + f.write( + ''' + LIAISON_UNIF = (''' ) + + for conn in [conn for conn in connections if len(conn['relatedGroupNames']) > 1]: + template = \ + ''' + _F( + GROUP_NO = {groupNames}, + DDL = ('DX', 'DY', 'DZ', 'DRX', 'DRY', 'DRZ') + ),''' + + context = { + 'groupNames': conn['relatedGroupNames'] + } + + f.write(template.format(**context)) + + f.write( + ''' + ),''' + ) + + if rigidLinkGroupNames: + f.write( + ''' + LIAISON_SOLIDE = (''' + ) + + for groupName in rigidLinkGroupNames: + template = \ + ''' + _F( + GROUP_MA = '{groupName}' + ),''' + + context = { + 'groupName': groupName + } + + f.write(template.format(**context)) + + f.write( + ''' + ),''' + ) + + f.write( + ''' +)''' + ) + + template = \ +''' +# STEP: DEFINE LOAD +gravLoad = AFFE_CHAR_MECA( + MODELE = model, + PESANTEUR = _F( + GRAVITE = {AccelOfGravity}, + DIRECTION = (0.0, 0.0, -1.0) + ) +) +''' + context = { + 'AccelOfGravity': AccelOfGravity, } f.write(template.format(**context)) - f.write( -''' - ),''' - ) - - # f.write( - # ''' - # ORIENTATION = (''' - # ) - # - # for el in [el for el in elements if el['geometryType'] == 'line']: - # - # template = \ - # ''' - # _F( - # GROUP_MA = '{group_name}', - # CARA = ('ANGL_VRIL',), - # VALE = {rotation} - # ),''' - # - # context = { - # 'group_name': getGroupName(str(el['ifcName'])), - # 'rotation': 0 # (el['rotation'],) - # } - # - # f.write(template.format(**context)) - # - # f.write( - # ''' - # ),''' - # ) - - f.write( -''' -)\n -''' - ) - - - f.write('# STEP: DEFINE GROUND BOUNDARY CONDITIONS') - - f.write( -''' -grdSupps = AFFE_CHAR_MECA( - MODELE = model, - DDL_IMPO = (''' - ) - - for conn in [conn for conn in connections if conn['appliedCondition']]: - f.write( - ''' - _F( - GROUP_NO = '%s',''' % conn['relatedGroupNames'][0] - ) - for dof in conn['appliedCondition']: - if conn['appliedCondition'][dof]: - f.write( - ''' - %s = 0,''' % (str(dof).upper()) - ) - f.write( - ''' - ),''' - ) - - f.write( - ''' - ),''' - ) - - if unifiedConnection: - f.write( - ''' - LIAISON_UNIF = (''' - ) - - for conn in [conn for conn in connections if len(conn['relatedGroupNames']) > 1]: - template = \ - ''' - _F( - GROUP_NO = {group_names}, - DDL = ('DX', 'DY', 'DZ', 'DRX', 'DRY', 'DRZ') - ),''' - - context = { - 'group_names': conn['relatedGroupNames'] - } - - f.write(template.format(**context)) f.write( - ''' - ),''' - ) - - if rigidLinkGroupNames: - f.write( - ''' - LIAISON_SOLIDE = (''' - ) - - for groupName in rigidLinkGroupNames: - template = \ - ''' - _F( - GROUP_MA = '{group_name}' - ),''' - - context = { - 'group_name': groupName - } - - f.write(template.format(**context)) - - f.write( - ''' - ),''' - ) - - f.write( - ''' -)''' - ) - - template = \ -''' -# STEP: DEFINE LOAD -exPESA = AFFE_CHAR_MECA( - MODELE = model, - PESANTEUR = _F( - GRAVITE = {AccelOfGravity}, - DIRECTION = (0.,0.,-1.) - ) -) -''' - context = { - 'AccelOfGravity': AccelOfGravity, - } - - f.write(template.format(**context)) - - - f.write( ''' # STEP: RUN ANALYSIS res_Bld = MECA_STATIQUE( @@ -448,91 +547,90 @@ res_Bld = MECA_STATIQUE( CARA_ELEM = element, EXCIT = ( _F( - CHARGE = grdSupps + CHARGE = liaisons ), _F( - CHARGE = exPESA + CHARGE = gravLoad ) ) ) ''' - ) + ) - -# f.write( -# ''' -# # STEP: POST-PROCESSING -# res_Bld = CALC_CHAMP( -# reuse = res_Bld, -# RESULTAT = res_Bld, -# CONTRAINTE = ('SIEF_ELNO', 'SIGM_ELNO', 'EFGE_ELNO',), -# FORCE = ('REAC_NODA', 'FORC_NODA',), -# ) -# ''' -# ) -# -# template = \ -# ''' -# # STEP: MASS EXTRACTION FOR EACH ASSEMBLE -# FaceMass = POST_ELEM( -# TITRE = 'TotMass', -# MODELE = model, -# CARA_ELEM = element, -# CHAM_MATER = material, -# MASS_INER = _F( -# GROUP_MA = {massList}, -# ), -# )\n''' -# -# context = { -# 'massList': massList, -# } -# -# f.write(template.format(**context)) -# -# f.write( -# ''' -# IMPR_TABLE( -# UNITE = 10, -# TABLE = FaceMass, -# SEPARATEUR = ',', -# NOM_PARA = ('LIEU', 'MASSE', 'CDG_X', 'CDG_Y', 'CDG_Z'), -# # FORMAT_R = '1PE15.6', -# ) -# ''' -# ) -# -# f.write( -# ''' -# # STEP: REACTION EXTRACTION AT THE BASE -# Reacs = POST_RELEVE_T( -# ACTION = _F( -# INTITULE = 'sumReac', -# GROUP_NO = 'grdSupps', -# RESULTAT = res_Bld, -# NOM_CHAM = 'REAC_NODA', -# RESULTANTE = ('DX','DY','DZ',), -# # MOMENT = ('DRX','DRY','DRZ',), -# # POINT = (0,0,0,), -# OPERATION = 'EXTRACTION', -# ), -# ) -# ''' -# ) -# -# f.write( -# ''' -# IMPR_TABLE( -# UNITE = 10, -# TABLE = Reacs, -# SEPARATEUR = ',', -# NOM_PARA = ('INTITULE', 'RESU', 'NOM_CHAM', 'INST', 'DX','DY','DZ'), -# # FORMAT_R = '1PE12.3', -# ) -# ''' -# ) -# - f.write( + # f.write( + # ''' + # # STEP: POST-PROCESSING + # res_Bld = CALC_CHAMP( + # reuse = res_Bld, + # RESULTAT = res_Bld, + # CONTRAINTE = ('SIEF_ELNO', 'SIGM_ELNO', 'EFGE_ELNO',), + # FORCE = ('REAC_NODA', 'FORC_NODA',), + # ) + # ''' + # ) + # + # template = \ + # ''' + # # STEP: MASS EXTRACTION FOR EACH ASSEMBLE + # FaceMass = POST_ELEM( + # TITRE = 'TotMass', + # MODELE = model, + # CARA_ELEM = element, + # CHAM_MATER = material, + # MASS_INER = _F( + # GROUP_MA = {massList}, + # ), + # )\n''' + # + # context = { + # 'massList': massList, + # } + # + # f.write(template.format(**context)) + # + # f.write( + # ''' + # IMPR_TABLE( + # UNITE = 10, + # TABLE = FaceMass, + # SEPARATEUR = ',', + # NOM_PARA = ('LIEU', 'MASSE', 'CDG_X', 'CDG_Y', 'CDG_Z'), + # # FORMAT_R = '1PE15.6', + # ) + # ''' + # ) + # + # f.write( + # ''' + # # STEP: REACTION EXTRACTION AT THE BASE + # Reacs = POST_RELEVE_T( + # ACTION = _F( + # INTITULE = 'sumReac', + # GROUP_NO = 'grdSupps', + # RESULTAT = res_Bld, + # NOM_CHAM = 'REAC_NODA', + # RESULTANTE = ('DX','DY','DZ',), + # # MOMENT = ('DRX','DRY','DRZ',), + # # POINT = (0,0,0,), + # OPERATION = 'EXTRACTION', + # ), + # ) + # ''' + # ) + # + # f.write( + # ''' + # IMPR_TABLE( + # UNITE = 10, + # TABLE = Reacs, + # SEPARATEUR = ',', + # NOM_PARA = ('INTITULE', 'RESU', 'NOM_CHAM', 'INST', 'DX','DY','DZ'), + # # FORMAT_R = '1PE12.3', + # ) + # ''' + # ) + # + f.write( ''' # STEP: DEFORMED SHAPE EXTRACTION IMPR_RESU( @@ -545,22 +643,94 @@ IMPR_RESU( ) ) ''' - ) + ) - f.write( + f.write( ''' # STEP: CONCLUDE STUDY FIN() ''' - ) + ) - f.close() + f.close() + + + def calculateConstraints(self, rel): + gr1 = self.getGroupName(rel['relatedConnection']) + gr2 = rel['groupName'] + o = np.array(rel['orientation']).transpose().tolist() + liaisons = { + 'groupNames': (gr1, gr1, gr1, gr2, gr2, gr2), + 'coeffs': [], + 'dofs': [] + } + if not rel['appliedCondition']: + rel['appliedCondition'] = { + 'dx': True, + 'dy': True, + 'dz': True, + 'drx': True, + 'dry': True, + 'drz': True + } + if rel['appliedCondition']['dx']: + liaisons['coeffs'].append((o[0][0], o[1][0], o[2][0], -o[0][0], -o[1][0], -o[2][0])) + liaisons['dofs'].append(('DX', 'DY', 'DZ', 'DX', 'DY', 'DZ')) + if rel['appliedCondition']['dy']: + liaisons['coeffs'].append((o[0][1], o[1][1], o[2][1], -o[0][1], -o[1][1], -o[2][1])) + liaisons['dofs'].append(('DX', 'DY', 'DZ', 'DX', 'DY', 'DZ')) + if rel['appliedCondition']['dz']: + liaisons['coeffs'].append((o[0][2], o[1][2], o[2][2], -o[0][2], -o[1][2], -o[2][2])) + liaisons['dofs'].append(('DX', 'DY', 'DZ', 'DX', 'DY', 'DZ')) + if rel['appliedCondition']['drx']: + liaisons['coeffs'].append((o[0][0], o[1][0], o[2][0], -o[0][0], -o[1][0], -o[2][0])) + liaisons['dofs'].append(('DRX', 'DRY', 'DRZ', 'DRX', 'DRY', 'DRZ')) + if rel['appliedCondition']['dry']: + liaisons['coeffs'].append((o[0][1], o[1][1], o[2][1], -o[0][1], -o[1][1], -o[2][1])) + liaisons['dofs'].append(('DRX', 'DRY', 'DRZ', 'DRX', 'DRY', 'DRZ')) + if rel['appliedCondition']['drz']: + liaisons['coeffs'].append((o[0][2], o[1][2], o[2][2], -o[0][2], -o[1][2], -o[2][2])) + liaisons['dofs'].append(('DRX', 'DRY', 'DRZ', 'DRX', 'DRY', 'DRZ')) + + rel['liaisons'] = liaisons + + def calculateRestraints(self, conn): + group = self.getGroupName(conn['ifcName']) + o = np.array(conn['orientation']).transpose().tolist() + liaisons = { + 'groupNames': (group, group, group), + 'coeffs': [], + 'dofs': [] + } + if not conn['appliedCondition']: + return + if conn['appliedCondition']['dx']: + liaisons['coeffs'].append((o[0][0], o[1][0], o[2][0])) + liaisons['dofs'].append(('DX', 'DY', 'DZ')) + if conn['appliedCondition']['dy']: + liaisons['coeffs'].append((o[0][1], o[1][1], o[2][1])) + liaisons['dofs'].append(('DX', 'DY', 'DZ')) + if conn['appliedCondition']['dz']: + liaisons['coeffs'].append((o[0][2], o[1][2], o[2][2])) + liaisons['dofs'].append(('DX', 'DY', 'DZ')) + if conn['appliedCondition']['drx']: + liaisons['coeffs'].append((o[0][0], o[1][0], o[2][0])) + liaisons['dofs'].append(('DRX', 'DRY', 'DRZ')) + if conn['appliedCondition']['dry']: + liaisons['coeffs'].append((o[0][1], o[1][1], o[2][1])) + liaisons['dofs'].append(('DRX', 'DRY', 'DRZ')) + if conn['appliedCondition']['drz']: + liaisons['coeffs'].append((o[0][2], o[1][2], o[2][2])) + liaisons['dofs'].append(('DRX', 'DRY', 'DRZ')) + + conn['liaisons'] = liaisons if __name__ == '__main__': - fileNames = ['cantilever_01', 'beam_01', 'portal_01', 'building_01', 'building-frame_01']; - files = [fileNames[3]] + fileNames = ['cantilever_01', 'portal_01']; + files = fileNames for fileName in files: - FILENAME = 'examples/' + fileName + '/' + fileName + '.json' - FILENAMEASTER = 'examples/' + fileName + '/CA_input_00.comm' - createCommFile(FILENAME, FILENAMEASTER) + BASE_PATH = '/home/jesusbill/Dev-Projects/github.com/IfcOpenShell/analysis-models/models/' + DATAFILENAME = BASE_PATH + fileName + '/' + fileName + '.json' + ASTERFILENAME = BASE_PATH + fileName + '/' + fileName + '.comm' + COMMANDFILE(DATAFILENAME, ASTERFILENAME) diff --git a/src/ifc2ca/scriptSalome.py b/src/ifc2ca/scriptSalome.py index c1dc6c0ec3..5905e0e676 100644 --- a/src/ifc2ca/scriptSalome.py +++ b/src/ifc2ca/scriptSalome.py @@ -4,15 +4,18 @@ import json import salome import salome_notebook import salome_version +import numpy as np from pprint import pprint -class MODEL(object): - def __init__(self, filename, meshSize): - self.filename = filename +class MODEL: + def __init__(self, dataFilename, medFilename, meshSize): + self.dataFilename = dataFilename + self.medFilename = medFilename self.meshSize = meshSize self.tolLoc = 0 self.mesh = None - self.create(filename) + self.meshNodes = None + self.create() def getGroupName(self, name): info = name.split('|') @@ -69,40 +72,10 @@ class MODEL(object): shapeType = 'FACE' return self.geompy.MakePartition(objects, [], [], [], self.geompy.ShapeType[shapeType], 0, [], 1) - def findIntersection(self, geometry, ecc): - # TO DO: implement a more general procedure - understand better how it works - if ecc['inX'] >= 0: - return geometry[1] - else: - return geometry[0] - # elemLength = self.length(geometry) - # pLocal = [ - # (ecc['pointOnElement'][0] - ecc['inX']) / elemLength, - # (ecc['pointOnElement'][1] - ecc['inY']) / elemLength, - # (ecc['pointOnElement'][2] - ecc['inZ']) / elemLength - # ] - # pLocalX = pLocal[0] - # - # if abs(pLocalX) < self.tolLoc*100: - # return geometry[0] - # elif abs(pLocalX - 1) < self.tolLoc*100: - # return geometry[1] - # elif pLocalX > 0 and pLocalX < 1: - # return [ - # (1 - pLocalX) * geometry[0][0] + pLocalX * geometry[1][0], - # (1 - pLocalX) * geometry[0][1] + pLocalX * geometry[1][1], - # (1 - pLocalX) * geometry[0][2] + pLocalX * geometry[1][2] - # ] - # else: - # pprint('Warning: Connection point not identified with a tight tolerance.') - # pprint('Tolerance needed is: %.2f' % max(abs(pLocalX), abs(pLocalX - 1))) - # tolMax = 0.1 - # if abs(pLocalX) < tolMax: - # return geometry[0] - # elif abs(pLocalX - 1) < tolMax: - # return geometry[1] - # else: - # pprint('Error: Connection point not identified with a tolerance of 10%') + # def getLinkGeometry(self, ecc, orientation, finalPoint): + # vector = np.array(orientation).transpose().dot(ecc['vector']) + # initialPoint = (np.array(finalPoint) - vector).tolist() + # return [initialPoint, finalPoint] def length(self, geometry): return (( @@ -111,13 +84,18 @@ class MODEL(object): (geometry[1][2] - geometry[0][2]) ** 2 \ ) ** 0.5) - def create(self, FILENAME): + def create(self): # Read data from input file - with open(FILENAME) as dataFile: + with open(self.dataFilename) as dataFile: data = json.load(dataFile) elements = data['elements'] connections = data['connections'] + # --> Delete this reference data and repopulate it with the objects + # while going through elements + for conn in connections: + conn['relatedElements'] = [] + # End <-- meshSize = self.meshSize @@ -168,72 +146,99 @@ class MODEL(object): # Loop 1 for el in elements: - el['elemObj'] = self.makeObject(el['geometry'], str(el['geometryType'])) + el['elemObj'] = self.makeObject(el['geometry'], el['geometryType']) el['connObjs'] = [None for _ in el['connections']] - el['linkObjs'] = [None for _ in el['connections']] for j,rel in enumerate(el['connections']): conn = [c for c in connections if c['ifcName'] == rel['relatedConnection']][0] - el['connObjs'][j] = self.makeObject(conn['geometry'], str(conn['geometryType'])) - if rel['eccentricity']: - pointOnElement = self.findIntersection(el['geometry'], rel['eccentricity']) - geometry = [pointOnElement, conn['geometry']] - el['linkObjs'][j] = self.makeObject(geometry, 'line') + conn['relatedElements'].append(rel) + if conn['geometryType'] == 'point': + if not rel['eccentricity']: + el['connObjs'][j] = self.makeObject(conn['geometry'], conn['geometryType']) + else: + pass + # geometry = self.getLinkGeometry(rel['eccentricity'], el['orientation'], conn['geometry']) + # el['linkObjs'][j] = self.makeObject(geometry, 'line') + elif conn['geometryType'] == 'line': + pass + elif conn['geometryType'] == 'surface': + pass - el['partObj'] = self.makePartition([el['elemObj']] + el['connObjs'] + [e for e in el['linkObjs'] if e], str(el['geometryType'])) + el['partObj'] = self.makePartition([el['elemObj']] + el['connObjs'], el['geometryType']) el['elemObj'] = geompy.GetInPlace(el['partObj'], el['elemObj']) for j,rel in enumerate(el['connections']): el['connObjs'][j] = geompy.GetInPlace(el['partObj'], el['connObjs'][j]) - if rel['eccentricity']: - el['linkObjs'][j] = geompy.GetInPlace(el['partObj'], el['linkObjs'][j]) + # if rel['eccentricity']: + # el['linkObjs'][j] = geompy.GetInPlace(el['partObj'], el['linkObjs'][j]) - # for conn in connections: - # if conn['appliedCondition']: - # conn['connObj'] = self.makeObject(conn['geometry'], str(conn['geometryType'])) + for conn in connections: + # if conn['appliedCondition']: + conn['connObj'] = self.makeObject(conn['geometry'], conn['geometryType']) # Make assemble of Building Object bldObjs = [] bldObjs.extend([el['partObj'] for el in elements]) + bldObjs.extend([conn['connObj'] for conn in connections]) # bldObjs.extend([conn['connObj'] for conn in connections if conn['appliedCondition']]) bldComp = geompy.MakeCompound(bldObjs) + # bldComp = geompy.MakePartition(bldObjs, [], [], [], self.geompy.ShapeType[buildingShapeType], 0, [], 1) geompy.addToStudy(bldComp, 'bldComp') # Loop 2 for el in elements: - # geompy.addToStudy(el['partObj'], self.getGroupName(str(el['ifcName']))) - - geompy.addToStudyInFather(el['partObj'], el['elemObj'], self.getGroupName(str(el['ifcName']))) + # geompy.addToStudy(el['partObj'], self.getGroupName(el['ifcName'])) + geompy.addToStudyInFather(el['partObj'], el['elemObj'], self.getGroupName(el['ifcName'])) for j,rel in enumerate(el['connections']): - geompy.addToStudyInFather(el['partObj'], el['connObjs'][j], self.getGroupName(str(el['ifcName'])) + '_0D_to_' + self.getGroupName(str(rel['relatedConnection']))) - if rel['eccentricity']: - geompy.addToStudyInFather(el['partObj'], el['linkObjs'][j], self.getGroupName(str(el['ifcName'])) + '_1D_to_' + self.getGroupName(str(rel['relatedConnection']))) + geompy.addToStudyInFather(el['partObj'], el['connObjs'][j], self.getGroupName(el['ifcName']) + '_0DC_' + self.getGroupName(rel['relatedConnection'])) + # if rel['eccentricity']: + # geompy.addToStudyInFather(el['partObj'], el['linkObjs'][j], self.getGroupName(el['ifcName']) + '_1DC_' + self.getGroupName(rel['relatedConnection'])) - # for conn in connections: - # if conn['appliedCondition']: - # # geompy.addToStudy(conn['connObj'], self.getGroupName(str(conn['ifcName']))) - # geompy.addToStudyInFather(conn['connObj'], conn['connObj'], self.getGroupName(str(conn['ifcName']))) + for conn in connections: + # if conn['appliedCondition']: + # geompy.addToStudy(conn['connObj'], self.getGroupName(conn['ifcName'])) + geompy.addToStudyInFather(conn['connObj'], conn['connObj'], self.getGroupName(conn['ifcName'])) elapsed_time = time.time() - init_time init_time += elapsed_time pprint('Building Geometry Defined in %g sec' % (elapsed_time)) + if len([e for e in elements if e['geometryType'] == 'line']) > 0: + buildingShapeType = 'EDGE' + if len([e for e in elements if e['geometryType'] == 'surface']) > 0: + buildingShapeType = 'FACE' + + # Define and add groups for all curve and surface members + if len([e for e in elements if e['geometryType'] == 'line']) > 0: + # Make compound of requested group + compoundTemp = geompy.MakeCompound([e['elemObj'] for e in elements if e['geometryType'] == 'line']) + # Define group object and add to study + curveCompound = geompy.GetInPlace(bldComp, compoundTemp) + geompy.addToStudyInFather(bldComp, curveCompound, 'CurveMembers') + + if len([e for e in elements if e['geometryType'] == 'surface']) > 0: + # Make compound of requested group + compoundTemp = geompy.MakeCompound([e['elemObj'] for e in elements if e['geometryType'] == 'surface']) + # Define group object and add to study + surfaceCompound = geompy.GetInPlace(bldComp, compoundTemp) + geompy.addToStudyInFather(bldComp, surfaceCompound, 'SurfaceMembers') + # Loop 3 for el in elements: # el['partObj'] = geompy.RestoreGivenSubShapes(bldComp, [el['partObj']], GEOM.FSM_GetInPlace, False, False)[0] - geompy.addToStudyInFather(bldComp, el['elemObj'], self.getGroupName(str(el['ifcName']))) + geompy.addToStudyInFather(bldComp, el['elemObj'], self.getGroupName(el['ifcName'])) for j,rel in enumerate(el['connections']): - geompy.addToStudyInFather(bldComp, el['connObjs'][j], self.getGroupName(str(el['ifcName'])) + '_0D_to_' + self.getGroupName(str(rel['relatedConnection']))) - if rel['eccentricity']: - el['linkObjs'][j].SetColor(SALOMEDS.Color(0, 0, 0)) - geompy.addToStudyInFather(bldComp, el['linkObjs'][j], self.getGroupName(str(el['ifcName'])) + '_1D_to_' + self.getGroupName(str(rel['relatedConnection']))) + geompy.addToStudyInFather(bldComp, el['connObjs'][j], self.getGroupName(el['ifcName']) + '_0DC_' + self.getGroupName(rel['relatedConnection'])) + # if rel['eccentricity']: + # el['linkObjs'][j].SetColor(SALOMEDS.Color(0, 0, 0)) + # geompy.addToStudyInFather(bldComp, el['linkObjs'][j], self.getGroupName(el['ifcName']) + '_1DC_' + self.getGroupName(rel['relatedConnection'])) - # for conn in connections: - # if conn['appliedCondition']: - # # conn['connObj'] = geompy.RestoreGivenSubShapes(bldComp, [conn['connObj']], GEOM.FSM_GetInPlace, False, False)[0] - # geompy.addToStudyInFather(bldComp, conn['connObj'], self.getGroupName(str(conn['ifcName']))) + for conn in connections: + # if conn['appliedCondition']: + # conn['connObj'] = geompy.RestoreGivenSubShapes(bldComp, [conn['connObj']], GEOM.FSM_GetInPlace, False, False)[0] + geompy.addToStudyInFather(bldComp, conn['connObj'], self.getGroupName(conn['ifcName'])) elapsed_time = time.time() - init_time init_time += elapsed_time @@ -252,12 +257,12 @@ class MODEL(object): smesh = smeshBuilder.New() else: smesh = smeshBuilder.New(theStudy) - Mesh_1 = smesh.Mesh(bldComp) - Regular1D = Mesh_1.Segment() - Local_Length_1 = Regular1D.LocalLength(meshSize, None, tolLoc) + bldMesh = smesh.Mesh(bldComp) + Regular_1D = bldMesh.Segment() + Local_Length_1 = Regular_1D.LocalLength(meshSize, None, tolLoc) if buildingShapeType == 'FACE': - NETGEN2D_ONLY = Mesh_1.Triangle(algo=smeshBuilder.NETGEN_2D) + NETGEN2D_ONLY = bldMesh.Triangle(algo=smeshBuilder.NETGEN_2D) NETGEN2D_Pars = NETGEN2D_ONLY.Parameters() NETGEN2D_Pars.SetMaxSize(meshSize) NETGEN2D_Pars.SetOptimize(1) @@ -268,49 +273,77 @@ class MODEL(object): NETGEN2D_Pars.SetSecondOrder(0) NETGEN2D_Pars.SetFuseEdges(254) - isDone = Mesh_1.Compute() + isDone = bldMesh.Compute() ## Set names of Mesh objects - smesh.SetName(Regular1D.GetAlgorithm(), 'Regular1D') + smesh.SetName(Regular_1D.GetAlgorithm(), 'Regular_1D') smesh.SetName(Local_Length_1, 'Local_Length_1') if buildingShapeType == 'FACE': smesh.SetName(NETGEN2D_ONLY.GetAlgorithm(), 'NETGEN2D_ONLY') smesh.SetName(NETGEN2D_Pars, 'NETGEN2D_Pars') - smesh.SetName(Mesh_1.GetMesh(), 'bldMesh') + smesh.SetName(bldMesh.GetMesh(), 'bldMesh') elapsed_time = time.time() - init_time init_time += elapsed_time pprint('Meshing Operations Completed in %g sec' % (elapsed_time)) + # Define and add groups for all curve and surface members + if len([e for e in elements if e['geometryType'] == 'line']) > 0: + tempgroup = bldMesh.GroupOnGeom(curveCompound, 'CurveMembers', SMESH.EDGE) + smesh.SetName(tempgroup, 'CurveMembers') + + if len([e for e in elements if e['geometryType'] == 'surface']) > 0: + tempgroup = bldMesh.GroupOnGeom(surfaceCompound, 'SurfaceMembers', SMESH.FACE) + smesh.SetName(tempgroup, 'SurfaceMembers') + # Define groups in Mesh for el in elements: if el['geometryType'] == 'line': shapeType = SMESH.EDGE if el['geometryType'] == 'surface': shapeType = SMESH.FACE - tempgroup = Mesh_1.GroupOnGeom(el['elemObj'], self.getGroupName(str(el['ifcName'])), shapeType) - smesh.SetName(tempgroup, self.getGroupName(str(el['ifcName']))) + tempgroup = bldMesh.GroupOnGeom(el['elemObj'], self.getGroupName(el['ifcName']), shapeType) + smesh.SetName(tempgroup, self.getGroupName(el['ifcName'])) for j,rel in enumerate(el['connections']): - tempgroup = Mesh_1.GroupOnGeom(el['connObjs'][j], self.getGroupName(str(el['ifcName'])) + '_0D_to_' + self.getGroupName(str(rel['relatedConnection'])), SMESH.NODE) - smesh.SetName(tempgroup, self.getGroupName(str(el['ifcName'])) + '_0D_to_' + self.getGroupName(str(rel['relatedConnection']))) - if rel['eccentricity']: - tempgroup = Mesh_1.GroupOnGeom(el['linkObjs'][j], self.getGroupName(str(el['ifcName'])) + '_1D_to_' + self.getGroupName(str(rel['relatedConnection'])), SMESH.EDGE) - smesh.SetName(tempgroup, self.getGroupName(str(el['ifcName'])) + '_1D_to_' + self.getGroupName(str(rel['relatedConnection']))) + tempgroup = bldMesh.GroupOnGeom(el['connObjs'][j], self.getGroupName(el['ifcName']) + '_0DC_' + self.getGroupName(rel['relatedConnection']), SMESH.NODE) + smesh.SetName(tempgroup, self.getGroupName(el['ifcName']) + '_0DC_' + self.getGroupName(rel['relatedConnection'])) + # if rel['eccentricity']: + # tempgroup = bldMesh.GroupOnGeom(el['linkObjs'][j], self.getGroupName(el['ifcName']) + '_1DC_' + self.getGroupName(rel['relatedConnection']), SMESH.EDGE) + # smesh.SetName(tempgroup, self.getGroupName(el['ifcName']) + '_1DC_' + self.getGroupName(rel['relatedConnection'])) - # for conn in connections: - # if conn['appliedCondition']: - # tempgroup = Mesh_1.GroupOnGeom(conn['connObj'], self.getGroupName(str(conn['ifcName'])), SMESH.NODE) - # smesh.SetName(tempgroup, self.getGroupName(str(conn['ifcName']))) + for conn in connections: + # if conn['appliedCondition']: + tempgroup = bldMesh.GroupOnGeom(conn['connObj'], self.getGroupName(conn['ifcName']), SMESH.NODE) + smesh.SetName(tempgroup, self.getGroupName(conn['ifcName'])) + nodesId = bldMesh.GetIDSource(tempgroup.GetNodeIDs(), SMESH.NODE) + tempgroup = bldMesh.Add0DElementsToAllNodes(nodesId, self.getGroupName(conn['ifcName'])) + smesh.SetName(tempgroup, self.getGroupName(conn['ifcName'])) - self.mesh = Mesh_1 + self.mesh = bldMesh + self.meshNodes = bldMesh.GetNodesId() elapsed_time = time.time() - init_time init_time += elapsed_time pprint('Mesh Groups Defined in %g sec' % (elapsed_time)) + try: + if NEW_SALOME: + bldMesh.ExportMED( + self.medFilename, + auto_groups = 0, + minor = 40, + overwrite = 1, + meshPart = None, + autoDimension = 0 + ) + else: + bldMesh.ExportMED(self.medFilename, 0, SMESH.MED_V2_2, 1, None, 0) + except: + pprint('ExportMED() failed. Invalid file name?') + if salome.sg.hasDesktop(): if NEW_SALOME: salome.sg.updateObjBrowser() @@ -322,13 +355,13 @@ class MODEL(object): pprint('ALL Operations Completed in %g sec' % (elapsed_time)) if __name__ == '__main__': - fileNames = ['cantilever_01', 'beam_01', 'portal_01', 'building_01', 'building-frame_01']; - files = [fileNames[3]] - meshSize = 200 + fileNames = ['cantilever_01', 'portal_01']; + files = fileNames + + meshSize = 0.1 for fileName in files: - # BASE_PATH = os.path.dirname(os.path.realpath('__file__')) - BASE_PATH = '/home/jesusbill/Dev-Projects/github.com/Jesusbill/ifc2ca' - FILENAME = BASE_PATH + '/examples/' + fileName + '/' + fileName + '.json' - FILENAMEMED = BASE_PATH + '/examples/' + fileName + '/bldMesh.med' - model = MODEL(FILENAME, meshSize) + BASE_PATH = '/home/jesusbill/Dev-Projects/github.com/IfcOpenShell/analysis-models/models/' + DATAFILENAME = BASE_PATH + fileName + '/' + fileName + '.json' + MEDFILENAME = BASE_PATH + fileName + '/' + fileName + '.med' + model = MODEL(DATAFILENAME, MEDFILENAME, meshSize)