New experimental IfcOpenShell IFC2X3<->IFC4 version migration utility

This commit is contained in:
Dion Moult
2020-11-25 13:13:46 +11:00
parent c057cb9aab
commit 3085c88647
3 changed files with 648 additions and 0 deletions
@@ -0,0 +1,251 @@
{
"IfcPerformanceHistory": {
"Identification": "LifeCyclePhase"
},
"IfcProcedure": {
"Identification": "ProcedureID",
"LongDescription": "ProcedureType",
"PredefinedType": "UserDefinedProcedureType"
},
"IfcTask": {
"Identification": "TaskId",
"LongDescription": "Status",
"Status": "WorkMethod",
"WorkMethod": "IsMilestone",
"IsMilestone": "Priority"
},
"IfcWorkPlan": {
"Identification": "Identifier",
"PredefinedType": "WorkControlType"
},
"IfcWorkSchedule": {
"Identification": "Identifier",
"PredefinedType": "WorkControlType"
},
"IfcSpace": {
"PredefinedType": "InteriorOrExteriorSpace"
},
"IfcTransportElement": {
"PredefinedType": "OperationType"
},
"IfcBuildingElementProxy": {
"PredefinedType": "CompositionType"
},
"IfcRamp": {
"PredefinedType": "ShapeType"
},
"IfcRelCoversSpaces": {
"RelatingSpace": "RelatedSpace"
},
"IfcRoof": {
"PredefinedType": "ShapeType"
},
"IfcStair": {
"PredefinedType": "ShapeType"
},
"IfcAsset": {
"Identification": "AssetID"
},
"IfcInventory": {
"PredefinedType": "InventoryType"
},
"IfcActionRequest": {
"Identification": "RequestID"
},
"IfcCostSchedule": {
"Identification": "SubmittedBy",
"PredefinedType": "PreparedBy",
"Status": "SubmittedOn",
"SubmittedOn": "Status",
"UpdateDate": "TargetUsers"
},
"IfcPermit": {
"Identification": "PermitID"
},
"IfcProjectOrder": {
"Identification": "ID"
},
"IfcConstructionEquipmentResource": {
"Identification": "ResourceIdentifier",
"LongDescription": "ResourceGroup",
"Usage": "ResourceConsumption",
"BaseCosts": "BaseQuantity"
},
"IfcConstructionMaterialResource": {
"Identification": "ResourceIdentifier",
"LongDescription": "ResourceGroup",
"Usage": "ResourceConsumption",
"BaseCosts": "BaseQuantity",
"BaseQuantity": "Suppliers",
"PredefinedType": "UsageRatio"
},
"IfcConstructionProductResource": {
"Identification": "ResourceIdentifier",
"LongDescription": "ResourceGroup",
"Usage": "ResourceConsumption",
"BaseCosts": "BaseQuantity"
},
"IfcCrewResource": {
"Identification": "ResourceIdentifier",
"LongDescription": "ResourceGroup",
"Usage": "ResourceConsumption",
"BaseCosts": "BaseQuantity"
},
"IfcLaborResource": {
"Identification": "ResourceIdentifier",
"LongDescription": "ResourceGroup",
"Usage": "ResourceConsumption",
"BaseCosts": "BaseQuantity",
"BaseQuantity": "SkillSet"
},
"IfcSubContractResource": {
"Identification": "ResourceIdentifier",
"LongDescription": "ResourceGroup",
"Usage": "ResourceConsumption",
"BaseCosts": "BaseQuantity",
"BaseQuantity": "SubContractor",
"PredefinedType": "JobDescription"
},
"IfcStructuralLinearAction": {
"ProjectedOrTrue": "CausedBy",
"PredefinedType": "ProjectedOrTrue"
},
"IfcStructuralPlanarAction": {
"ProjectedOrTrue": "CausedBy",
"PredefinedType": "ProjectedOrTrue"
},
"IfcReinforcingBar": {
"PredefinedType": "BarRole"
},
"IfcOrganization": {
"Identification": "Id"
},
"IfcPerson": {
"Identification": "Id"
},
"IfcApproval": {
"Identifier": "Description",
"Name": "ApprovalDateTime",
"Description": "ApprovalStatus",
"TimeOfApproval": "ApprovalLevel",
"Status": "ApprovalQualifier",
"Level": "Name",
"Qualifier": "Identifier"
},
"IfcApprovalRelationship": {
"Name": "RelatedApproval",
"Description": "RelatingApproval",
"RelatingApproval": "Description",
"RelatedApprovals": "Name"
},
"IfcObjective": {
"LogicalAggregator": "ResultValues"
},
"IfcCostValue": {
"Category": "CostType"
},
"IfcCurrencyRelationship": {
"Name": "RelatingMonetaryUnit",
"Description": "RelatedMonetaryUnit",
"RelatingMonetaryUnit": "ExchangeRate",
"RelatedMonetaryUnit": "RateDateTime",
"ExchangeRate": "RateSource"
},
"IfcClassificationReference": {
"Identification": "ItemReference"
},
"IfcDocumentInformation": {
"Identification": "DocumentId",
"Location": "DocumentReferences"
},
"IfcDocumentInformationRelationship": {
"Name": "RelatingDocument",
"Description": "RelatedDocuments",
"RelatingDocument": "RelationshipType"
},
"IfcDocumentReference": {
"Identification": "ItemReference"
},
"IfcLibraryInformation": {
"Location": "LibraryReference"
},
"IfcLibraryReference": {
"Identification": "ItemReference"
},
"IfcMaterialProperties": {
"Name": "Material"
},
"IfcBlobTexture": {
"Mode": "TextureType",
"Parameter": "RasterFormat",
"RasterFormat": "RasterCode"
},
"IfcExternallyDefinedHatchStyle": {
"Identification": "ItemReference"
},
"IfcExternallyDefinedSurfaceStyle": {
"Identification": "ItemReference"
},
"IfcExternallyDefinedTextFont": {
"Identification": "ItemReference"
},
"IfcImageTexture": {
"Mode": "TextureType",
"Parameter": "UrlReference"
},
"IfcPixelTexture": {
"Mode": "TextureType",
"Parameter": "Width",
"Width": "Height",
"Height": "ColourComponents",
"ColourComponents": "Pixel"
},
"IfcTextureCoordinateGenerator": {
"Maps": "Mode",
"Mode": "Parameter"
},
"IfcTextureMap": {
"Maps": "TextureMaps"
},
"IfcAsymmetricIShapeProfileDef": {
"BottomFlangeWidth": "OverallWidth",
"BottomFlangeThickness": "FlangeThickness",
"BottomFlangeFilletRadius": "FilletRadius",
"BottomFlangeEdgeRadius": "CentreOfGravityInY"
},
"IfcProfileProperties": {
"Name": "ProfileName",
"Description": "ProfileDefinition"
},
"IfcPropertyDependencyRelationship": {
"Name": "DependingProperty",
"Description": "DependantProperty",
"DependingProperty": "Name",
"DependantProperty": "Description"
},
"IfcBoundaryEdgeCondition": {
"TranslationalStiffnessByLengthX": "LinearStiffnessByLengthX",
"TranslationalStiffnessByLengthY": "LinearStiffnessByLengthY",
"TranslationalStiffnessByLengthZ": "LinearStiffnessByLengthZ"
},
"IfcBoundaryFaceCondition": {
"TranslationalStiffnessByAreaX": "LinearStiffnessByAreaX",
"TranslationalStiffnessByAreaY": "LinearStiffnessByAreaY",
"TranslationalStiffnessByAreaZ": "LinearStiffnessByAreaZ"
},
"IfcBoundaryNodeCondition": {
"TranslationalStiffnessX": "LinearStiffnessX",
"TranslationalStiffnessY": "LinearStiffnessY",
"TranslationalStiffnessZ": "LinearStiffnessZ"
},
"IfcBoundaryNodeConditionWarping": {
"TranslationalStiffnessX": "LinearStiffnessX",
"TranslationalStiffnessY": "LinearStiffnessY",
"TranslationalStiffnessZ": "LinearStiffnessZ"
},
"IfcStructuralLoadTemperature": {
"DeltaTConstant": "DeltaT_Constant",
"DeltaTY": "DeltaT_Y",
"DeltaTZ": "DeltaT_Z"
}
}
@@ -0,0 +1,213 @@
{
"IfcComplexPropertyTemplate": "",
"IfcProjectLibrary": "",
"IfcPropertySetTemplate": "",
"IfcRelAssignsToGroupByFactor": "",
"IfcRelDeclares": "",
"IfcRelDefinesByObject": "",
"IfcRelDefinesByTemplate": "",
"IfcSimplePropertyTemplate": "",
"IfcEvent": "",
"IfcEventType": "",
"IfcProcedureType": "",
"IfcTaskType": "",
"IfcWorkCalendar": "",
"IfcCivilElement": "",
"IfcCivilElementType": "",
"IfcElementAssemblyType": "",
"IfcExternalSpatialElement": "IfcSpace",
"IfcGeographicElement": "",
"IfcGeographicElementType": "",
"IfcOpeningStandardCase": "",
"IfcRelInterferesElements": "",
"IfcRelSpaceBoundary1stLevel": "",
"IfcRelSpaceBoundary2ndLevel": "",
"IfcSpatialZone": "",
"IfcSpatialZoneType": "",
"IfcBeamStandardCase": "",
"IfcBuildingSystem": "",
"IfcChimney": "",
"IfcChimneyType": "",
"IfcColumnStandardCase": "",
"IfcDoorStandardCase": "",
"IfcDoorType": "",
"IfcMemberStandardCase": "",
"IfcPlateStandardCase": "",
"IfcRampType": "",
"IfcRoofType": "",
"IfcShadingDevice": "",
"IfcShadingDeviceType": "",
"IfcSlabElementedCase": "",
"IfcSlabStandardCase": "",
"IfcStairType": "",
"IfcWallElementedCase": "",
"IfcWindowStandardCase": "",
"IfcWindowType": "",
"IfcDistributionCircuit": "",
"IfcDistributionSystem": "",
"IfcBuildingElementPartType": "",
"IfcFurniture": "",
"IfcSystemFurnitureElement": "",
"IfcActuator": "",
"IfcAlarm": "",
"IfcController": "",
"IfcFlowInstrument": "",
"IfcSensor": "",
"IfcUnitaryControlElement": "",
"IfcUnitaryControlElementType": "",
"IfcConstructionEquipmentResourceType": "",
"IfcConstructionMaterialResourceType": "",
"IfcConstructionProductResourceType": "",
"IfcCrewResourceType": "",
"IfcLaborResourceType": "",
"IfcSubContractResourceType": "",
"IfcAudioVisualAppliance": "",
"IfcAudioVisualApplianceType": "",
"IfcCableCarrierFitting": "",
"IfcCableCarrierSegment": "",
"IfcCableFitting": "",
"IfcCableFittingType": "",
"IfcCableSegment": "",
"IfcCommunicationsAppliance": "",
"IfcCommunicationsApplianceType": "",
"IfcElectricAppliance": "",
"IfcElectricDistributionBoard": "",
"IfcElectricDistributionBoardType": "",
"IfcElectricFlowStorageDevice": "",
"IfcElectricGenerator": "",
"IfcElectricMotor": "",
"IfcElectricTimeControl": "",
"IfcJunctionBox": "",
"IfcLamp": "",
"IfcLightFixture": "",
"IfcMotorConnection": "",
"IfcOutlet": "",
"IfcProtectiveDevice": "",
"IfcProtectiveDeviceTrippingUnit": "",
"IfcProtectiveDeviceTrippingUnitType": "",
"IfcSolarDevice": "",
"IfcSolarDeviceType": "",
"IfcSwitchingDevice": "",
"IfcTransformer": "",
"IfcAirTerminal": "",
"IfcAirTerminalBox": "",
"IfcAirToAirHeatRecovery": "",
"IfcBoiler": "",
"IfcBurner": "",
"IfcBurnerType": "",
"IfcChiller": "",
"IfcCoil": "",
"IfcCompressor": "",
"IfcCondenser": "",
"IfcCooledBeam": "",
"IfcCoolingTower": "",
"IfcDamper": "",
"IfcDuctFitting": "",
"IfcDuctSegment": "",
"IfcDuctSilencer": "",
"IfcEngine": "",
"IfcEngineType": "",
"IfcEvaporativeCooler": "",
"IfcEvaporator": "",
"IfcFan": "",
"IfcFilter": "",
"IfcFlowMeter": "",
"IfcHeatExchanger": "",
"IfcHumidifier": "",
"IfcMedicalDevice": "",
"IfcMedicalDeviceType": "",
"IfcPipeFitting": "",
"IfcPipeSegment": "",
"IfcPump": "",
"IfcSpaceHeater": "",
"IfcTank": "",
"IfcTubeBundle": "",
"IfcUnitaryEquipment": "",
"IfcValve": "",
"IfcVibrationIsolator": "",
"IfcFireSuppressionTerminal": "",
"IfcInterceptor": "",
"IfcInterceptorType": "",
"IfcSanitaryTerminal": "",
"IfcStackTerminal": "",
"IfcWasteTerminal": "",
"IfcStructuralCurveAction": "",
"IfcStructuralCurveReaction": "",
"IfcStructuralLoadCase": "",
"IfcStructuralSurfaceAction": "",
"IfcStructuralSurfaceReaction": "",
"IfcFootingType": "",
"IfcPileType": "",
"IfcReinforcingBarType": "",
"IfcReinforcingMeshType": "",
"IfcSurfaceFeature": "",
"IfcTendonAnchorType": "",
"IfcTendonType": "",
"IfcVoidingFeature": "",
"IfcResourceApprovalRelationship": "",
"IfcAppliedValue": "",
"IfcReference": "",
"IfcResourceConstraintRelationship": "",
"IfcEventTime": "",
"IfcLagTime": "",
"IfcRecurrencePattern": "",
"IfcResourceTime": "",
"IfcTaskTime": "",
"IfcTaskTimeRecurring": "",
"IfcTimePeriod": "",
"IfcWorkTime": "",
"IfcExternalReferenceRelationship": "",
"IfcConnectionVolumeGeometry": "",
"IfcAdvancedBrep": "",
"IfcAdvancedBrepWithVoids": "",
"IfcCartesianPointList3D": "",
"IfcExtrudedAreaSolidTapered": "",
"IfcFixedReferenceSweptAreaSolid": "",
"IfcRevolvedAreaSolidTapered": "",
"IfcSweptDiskSolidPolygonal": "",
"IfcTriangulatedFaceSet": "",
"IfcBoundaryCurve": "",
"IfcBSplineCurveWithKnots": "",
"IfcBSplineSurfaceWithKnots": "",
"IfcCompositeCurveOnSurface": "",
"IfcCurveBoundedSurface": "",
"IfcCylindricalSurface": "",
"IfcOuterBoundaryCurve": "",
"IfcPcurve": "",
"IfcRationalBSplineCurveWithKnots": "",
"IfcRationalBSplineSurfaceWithKnots": "",
"IfcReparametrisedCompositeCurveSegment": "",
"IfcMaterialConstituent": "",
"IfcMaterialConstituentSet": "",
"IfcMaterialLayerWithOffsets": "",
"IfcMaterialProfile": "",
"IfcMaterialProfileSet": "",
"IfcMaterialProfileSetUsage": "",
"IfcMaterialProfileSetUsageTapering": "",
"IfcMaterialProfileWithOffsets": "",
"IfcMaterialRelationship": "",
"IfcConversionBasedUnitWithOffset": "",
"IfcVector": "",
"IfcColourRgbList": "",
"IfcIndexedColourMap": "",
"IfcIndexedTriangleTextureMap": "",
"IfcTextureVertexList": "",
"IfcMirroredProfileDef": "",
"IfcTable": "",
"IfcMapConversion": "",
"IfcProjectedCRS": "",
"IfcStructuralLoadConfiguration": "",
"IfcSurfaceReinforcementArea": "",
"IfcAdvancedFace": "",
"IfcTableColumn": "",
"IfcCartesianPointList2D": "",
"IfcIndexedPolyCurve": "",
"IfcIndexedPolygonalFace": "",
"IfcIndexedPolygonalFaceWithVoids": "",
"IfcPolygonalFaceSet": "",
"IfcIntersectionCurve": "",
"IfcSeamCurve": "",
"IfcSphericalSurface": "",
"IfcSurfaceCurve": "",
"IfcToroidalSurface": ""
}
@@ -0,0 +1,184 @@
import os
import json
import time
import ifcopenshell
# This is highly experimental and incomplete, however, it may work for simple datasets.
# In this simple implementation, we only support 2X3<->4 right now
cwd = os.path.dirname(os.path.realpath(__file__))
class Migrator:
def __init__(self):
self.migrated_ids = {}
self.class_4_to_2x3 = json.load(open(os.path.join(cwd, "class_4_to_2x3.json"), "r"))
# IFC4 classes, and their IFC4 attribute : IFC2X3 attributes
self.attribute_4_to_2x3 = json.load(open(os.path.join(cwd, "attribute_4_to_2x3.json"), "r"))
self.default_values = {
"ChangeAction": "NOCHANGE",
"CompositionType": "ELEMENT",
"CrossSectionArea": 1,
"DataValue": 0,
"DefinedValues": [0],
"DefiningValues": [0],
"DestabilizingLoad": False,
"Edition": "",
"EndParam": 1.0,
"EnumerationValues": [0],
"GeodeticDatum": "",
"Intent": "",
"IsHeading": False,
"ListValues": [0],
"LongitudinalBarCrossSectionArea": 1,
"LongitudinalBarNominalDiameter": 1,
"LongitudinalBarSpacing": 1,
"Name": "",
"NominalDiameter": 1,
"PredefinedType": "NOTDEFINED",
"RowCells": [0],
"SequenceType": "NOTDEFINED",
"Source": "",
"StartParam": 0.0,
"TransverseBarCrossSectionArea": 1,
"TransverseBarNominalDiameter": 1,
"TransverseBarSpacing": 1,
# Manual additions from experience
"InteriorOrExteriorSpace": "NOTDEFINED",
}
self.default_entities = {
"CurrentValue": None,
"DepreciatedValue": None,
"Jurisdiction": None,
"OriginalValue": None,
"Owner": None,
"OwnerHistory": None,
"Position": None,
"PropertyReference": None,
"RepresentationContexts": None,
"ResponsiblePerson": None,
"ResponsiblePersons": None,
"Rows": None,
"TotalReplacementCost": None,
"UnitsInContext": None,
"User": None,
}
def migrate(self, element, new_file):
try:
return new_file.by_id(self.migrated_ids[element.id()])
except:
pass
print("Migrating", element)
schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(new_file.schema)
new_element = self.migrate_class(element, new_file)
print("Migrated class from {} to {}".format(element, new_element))
new_element_schema = schema.declaration_by_name(new_element.is_a())
if not hasattr(new_element_schema, "all_attributes"):
return element # The element has no attributes, so migration is done
new_element = self.migrate_attributes(element, new_file, new_element, new_element_schema)
self.migrated_ids[element.id()] = new_element.id()
return new_element
def migrate_class(self, element, new_file):
try:
new_element = new_file.create_entity(element.is_a())
except:
# The element does not exist in this schema
# Complex migration is not yet supported (e.g. polygonal face set to faceted brep)
if new_file.schema == "IFC2X3":
new_element = new_file.create_entity(self.class_4_to_2x3[element.is_a()])
elif new_file.schema == "IFC4":
print("Class migration to IFC4 not yet supported for", element)
return new_element
def migrate_attributes(self, element, new_file, new_element, new_element_schema):
for i, attribute in enumerate(new_element_schema.all_attributes()):
if new_element_schema.derived()[i]:
continue
self.migrate_attribute(attribute, element, new_file, new_element, new_element_schema)
return new_element
def migrate_attribute(self, attribute, element, new_file, new_element, new_element_schema):
print("Migrating attribute", element, new_element, attribute.name())
if hasattr(element, attribute.name()):
value = getattr(element, attribute.name())
print("Attribute names matched", value)
elif new_file.schema == "IFC2X3":
# IFC4 to IFC2X3: We know the IFC2X3 attribute name, but not its IFC4 equivalent
print("Searching for an equivalent", new_element, attribute.name())
try:
equivalent_map = self.attribute_4_to_2x3[new_element.is_a()]
equivalent = list(equivalent_map.keys())[list(equivalent_map.values()).index(attribute.name())]
if hasattr(element, equivalent):
print("Equivalent found", equivalent)
value = getattr(element, equivalent)
else:
return
except:
print("Unable to find equivalent attribute of {} to migrate from {} to {}".format(attribute.name(), element, new_element))
return # We tried our best
elif new_file.schema == "IFC4":
# IFC2X3 to IFC4: We know the IFC4 attribute name, but not its IFC2X3 equivalent
print("Searching for an equivalent", element, new_element, attribute.name())
try:
equivalent = self.attribute_4_to_2x3[new_element.is_a()][attribute.name()]
print("Searching for equivalent", equivalent)
if hasattr(element, equivalent):
value = getattr(element, equivalent)
else:
return
except:
print("Unable to find equivalent attribute of {} to migrate from {} to {}".format(attribute.name(), element, new_element))
return # We tried our best
print("Continuing migration of {} to migrate from {} to {}".format(attribute.name(), element, new_element))
if value is None and not attribute.optional():
value = self.generate_default_value(attribute, new_file)
if value is None:
print("Failed to generate default value for {} on {}".format(attribute.name(), element))
elif isinstance(value, ifcopenshell.entity_instance):
value = self.migrate(value, new_file)
elif isinstance(value, (list, tuple)):
if value and isinstance(value[0], ifcopenshell.entity_instance):
new_value = []
for item in value:
new_value.append(self.migrate(item, new_file))
value = new_value
setattr(new_element, attribute.name(), value)
def generate_default_value(self, attribute, new_file):
if attribute.name() in self.default_values:
return self.default_values[attribute.name()]
elif self.default_entities[attribute.name()]:
return self.default_entities[attribute.name()]
elif attribute.name() == "OwnerHistory":
self.default_entities[attribute.name()] = new_file.create_entity("IfcOwnerHistory", **{
"OwningUser": new_file.create_entity("IfcPersonAndOrganization", **{
"ThePerson": new_file.create_entity("IfcPerson"),
"TheOrganization": new_file.create_entity("IfcOrganization", **{
"Name": "IfcOpenShell Migrator"
})
}),
"OwningApplication": new_file.create_entity("IfcApplication", **{
"ApplicationDeveloper": new_file.create_entity("IfcOrganization", **{
"Name": "IfcOpenShell Migrator"
}),
"Version": "Works for me",
"ApplicationFullName": "IfcOpenShell Migrator",
"ApplicationIdentifier": "IfcOpenShell Migrator",
}),
"ChangeAction": "NOCHANGE",
"CreationDate": int(time.time())
})
return self.default_entities[attribute.name()]
# f = ifcopenshell.open("/home/dion/migrate.ifc")
# g = ifcopenshell.file(schema="IFC4")
# migrator = Migrator()
# for e in f:
# print(e)
# print(migrator.migrate(e, g))
# g.write("/home/dion/migrate2.ifc")