mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-11 02:02:22 +00:00
451 lines
19 KiB
Python
451 lines
19 KiB
Python
# Ifc2CA - IFC Code_Aster utility
|
|
# Copyright (C) 2020, 2021, 2023, 2024 Ioannis P. Christovasilis <ipc@aethereng.com>
|
|
#
|
|
# This file is part of Ifc2CA.
|
|
#
|
|
# Ifc2CA is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU Lesser General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# Ifc2CA is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU Lesser General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Lesser General Public License
|
|
# along with Ifc2CA. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
import itertools
|
|
|
|
import ifcopenshell as ios
|
|
import meshio
|
|
import numpy as np
|
|
|
|
flatten = itertools.chain.from_iterable
|
|
|
|
|
|
def get_element_data(model, name, element):
|
|
if element["geometry_type"] == "Edge":
|
|
for i, cell_block in enumerate(model.cells):
|
|
if cell_block.type == "line":
|
|
cell_tags = model.cell_data["cell_tags"][i]
|
|
break
|
|
rows = []
|
|
for i_row, i in enumerate(cell_tags):
|
|
if i == 0:
|
|
continue
|
|
tags = model.cell_tags[i]
|
|
for tag in tags:
|
|
if tag == name:
|
|
# print(i_row, i)
|
|
rows.append(i_row)
|
|
break
|
|
|
|
points = list(set(flatten([cell_block.data[c] for c in rows])))
|
|
points.sort(key=lambda p: np.linalg.norm(model.points[p] - np.array(element["origin"])))
|
|
coords = [np.round(model.points[p], 4).tolist() for p in points]
|
|
local_coords = [
|
|
[float(round(np.linalg.norm(model.points[p] - np.array(element["origin"])), 4))] for p in points
|
|
]
|
|
|
|
return {
|
|
"name": name,
|
|
"points": points,
|
|
"coords": coords,
|
|
"local_coords": local_coords,
|
|
}
|
|
|
|
elif element["geometry_type"] == "Face":
|
|
triangle_cell_tags = None
|
|
quad_cell_tags = None
|
|
for i, cell_block in enumerate(model.cells):
|
|
if cell_block.type == "triangle":
|
|
triangle_cell_tags = model.cell_data["cell_tags"][i]
|
|
break
|
|
|
|
if triangle_cell_tags is not None:
|
|
rows = []
|
|
for i_row, i in enumerate(triangle_cell_tags):
|
|
if i == 0:
|
|
continue
|
|
tags = model.cell_tags[i]
|
|
for tag in tags:
|
|
if tag == name:
|
|
# print(i_row, i)
|
|
rows.append(i_row)
|
|
break
|
|
if not len(rows):
|
|
points = []
|
|
else:
|
|
points = list(flatten([cell_block.data[c] for c in rows]))
|
|
|
|
for i, cell_block in enumerate(model.cells):
|
|
if cell_block.type == "quad":
|
|
quad_cell_tags = model.cell_data["cell_tags"][i]
|
|
break
|
|
|
|
if quad_cell_tags is not None:
|
|
rows = []
|
|
for i_row, i in enumerate(quad_cell_tags):
|
|
if i == 0:
|
|
continue
|
|
tags = model.cell_tags[i]
|
|
for tag in tags:
|
|
if tag == name:
|
|
# print(i_row, i)
|
|
rows.append(i_row)
|
|
break
|
|
if len(rows):
|
|
points.extend(list(flatten([cell_block.data[c] for c in rows])))
|
|
|
|
points = list(set(points))
|
|
points.sort()
|
|
coords = [model.points[p].tolist() for p in points]
|
|
local_coords = [
|
|
np.round(np.array(element["orientation"]).dot(model.points[p] - np.array(element["origin"])), 4).tolist()[
|
|
:2
|
|
]
|
|
for p in points
|
|
]
|
|
|
|
return {
|
|
"name": name,
|
|
"points": points,
|
|
"coords": coords,
|
|
"local_coords": local_coords,
|
|
}
|
|
|
|
|
|
def get_element_result_data(model, field_label, name, element, field_type):
|
|
points = get_element_data(model, name, element)["points"]
|
|
if field_type == "InternalForces":
|
|
if element["geometry_type"] == "Edge":
|
|
return {
|
|
"N": [round(model.point_data[field_label][p][0], 4) for p in points],
|
|
"VY": [round(model.point_data[field_label][p][1], 4) for p in points],
|
|
"VZ": [round(model.point_data[field_label][p][2], 4) for p in points],
|
|
"MT": [round(model.point_data[field_label][p][3], 4) for p in points],
|
|
"MFY": [round(model.point_data[field_label][p][4], 4) for p in points],
|
|
"MFZ": [round(model.point_data[field_label][p][5], 4) for p in points],
|
|
}
|
|
|
|
elif element["geometry_type"] == "Face":
|
|
if len(model.point_data[field_label][points[0]]) == 8:
|
|
offset = 0
|
|
elif len(model.point_data[field_label][points[0]]) == 14:
|
|
offset = 6
|
|
else:
|
|
assert (
|
|
False
|
|
), f"Internal force field with {len(model.point_data[field_label][points[0]])} field values for {field_label} and {element['Name']} "
|
|
|
|
return {
|
|
"NXX": [round(model.point_data[field_label][p][offset + 0], 4) for p in points],
|
|
"NYY": [round(model.point_data[field_label][p][offset + 1], 4) for p in points],
|
|
"NXY": [round(model.point_data[field_label][p][offset + 2], 4) for p in points],
|
|
"MXX": [round(model.point_data[field_label][p][offset + 3], 4) for p in points],
|
|
"MYY": [round(model.point_data[field_label][p][offset + 4], 4) for p in points],
|
|
"MXY": [round(model.point_data[field_label][p][offset + 5], 4) for p in points],
|
|
"QX": [round(model.point_data[field_label][p][offset + 6], 4) for p in points],
|
|
"QY": [round(model.point_data[field_label][p][offset + 7], 4) for p in points],
|
|
}
|
|
|
|
if field_type == "Displacements":
|
|
return {
|
|
"DX": [round(model.point_data[field_label][p][0], 4) for p in points],
|
|
"DY": [round(model.point_data[field_label][p][1], 4) for p in points],
|
|
"DZ": [round(model.point_data[field_label][p][2], 4) for p in points],
|
|
"DRX": [round(model.point_data[field_label][p][3], 4) for p in points],
|
|
"DRY": [round(model.point_data[field_label][p][4], 4) for p in points],
|
|
"DRZ": [round(model.point_data[field_label][p][5], 4) for p in points],
|
|
}
|
|
|
|
|
|
def results_to_ifc(ifc_file, ifc_model, rmed_path, global_case, field_types, data):
|
|
if not rmed_path.exists():
|
|
print(f"Med file with results not found for case_instant: {global_case}")
|
|
return
|
|
|
|
result = meshio.read(rmed_path, "med")
|
|
if global_case == "LC":
|
|
model_cases = data["load_cases"]
|
|
elif global_case == "COMB":
|
|
model_cases = data["load_combinations"]
|
|
for field in field_types:
|
|
if field == "InternalForces":
|
|
_parsed_data = internal_forces_to_ifc(ifc_file, ifc_model, result, model_cases, data["elements"])
|
|
elif field == "Displacements":
|
|
_parsed_data = displacements_to_ifc(ifc_file, ifc_model, result, model_cases, data["elements"])
|
|
|
|
|
|
def internal_forces_to_ifc(ifc_file, ifc_model, result, model_cases, elements):
|
|
result_cases = [dict() for _ in model_cases]
|
|
field_cases = [f"ELEMENT_FORCE[{i}] - {i + 1}" for i in range(len(result_cases))]
|
|
|
|
# Create Result Groups for load case_instance combinations
|
|
for iCase, case_instance in enumerate(model_cases):
|
|
result_cases[iCase]["case_instance"] = ifc_file.create_entity(
|
|
"IfcStructuralResultGroup",
|
|
**{
|
|
"GlobalId": ios.guid.new(),
|
|
"Name": "Internal Forces for " + case_instance["Name"],
|
|
"TheoryType": "FIRST_ORDER_THEORY",
|
|
"ResultForLoadGroup": ifc_file.by_id(case_instance["id"]),
|
|
"IsLinear": True,
|
|
},
|
|
)
|
|
|
|
result_cases[iCase]["assignment"] = ifc_file.create_entity(
|
|
"IfcRelAssignsToGroup",
|
|
**{
|
|
"GlobalId": ios.guid.new(),
|
|
"RelatedObjects": [],
|
|
"RelatingGroup": result_cases[iCase]["case_instance"],
|
|
},
|
|
)
|
|
|
|
if ifc_model.HasResults:
|
|
ifc_model.HasResults += tuple([result["case_instance"] for result in result_cases])
|
|
else:
|
|
ifc_model.HasResults = tuple([result["case_instance"] for result in result_cases])
|
|
|
|
data = []
|
|
for _, element in enumerate(elements):
|
|
group_name = getGroupName(element["ref_id"])
|
|
name = element["Name"]
|
|
info = get_element_data(result, group_name, element)
|
|
assert len(info["coords"]) >= 2
|
|
for iCase, field_case in enumerate(field_cases):
|
|
forces = get_element_result_data(result, field_case, group_name, element, field_type="InternalForces")
|
|
reaction = ifc_file.create_entity(
|
|
"IfcStructuralCurveReaction" if element["geometry_type"] == "Edge" else "IfcStructuralSurfaceReaction",
|
|
**{
|
|
"GlobalId": ios.guid.new(),
|
|
"Name": "Internal Forces for " + model_cases[iCase]["Name"] + f" on {name}",
|
|
# "AppliedLoad": load["ifcLoad"],
|
|
"GlobalOrLocal": "LOCAL_COORDS",
|
|
"PredefinedType": "DISCRETE",
|
|
},
|
|
)
|
|
result_cases[iCase]["assignment"].RelatedObjects += (reaction,)
|
|
|
|
ifc_file.create_entity(
|
|
"IfcRelConnectsStructuralActivity",
|
|
**{
|
|
"GlobalId": ios.guid.new(),
|
|
"RelatingElement": ifc_file.by_id(element["id"]),
|
|
"RelatedStructuralActivity": reaction,
|
|
},
|
|
)
|
|
|
|
reaction.AppliedLoad = ifc_file.create_entity(
|
|
"IfcStructuralLoadConfiguration",
|
|
**{
|
|
"Name": "Internal Forces for " + model_cases[iCase]["Name"] + f" on {name}",
|
|
"Values": [],
|
|
"Locations": tuple([tuple(node) for node in info["local_coords"]]),
|
|
},
|
|
)
|
|
|
|
if element["geometry_type"] == "Edge":
|
|
for iNode, node in enumerate(info["coords"]):
|
|
location = f"({node[0]}, {node[1]}, {node[2]})"
|
|
distance = info["local_coords"][iNode][0]
|
|
|
|
N = forces["N"][iNode]
|
|
VY = forces["VY"][iNode]
|
|
VZ = forces["VZ"][iNode]
|
|
MT = forces["MT"][iNode]
|
|
MFY = forces["MFY"][iNode]
|
|
MFZ = forces["MFZ"][iNode]
|
|
|
|
data.append([name, f"LCC-{iCase + 1} @ {distance}", location, N, VY, VZ, MT, MFY, MFZ])
|
|
|
|
pointValue = ifc_file.create_entity(
|
|
"IfcStructuralLoadSingleForce",
|
|
**{
|
|
"Name": "Internal Forces for " + model_cases[iCase]["Name"] + f" @ {distance} on {name}",
|
|
"ForceX": N,
|
|
"ForceY": VY,
|
|
"ForceZ": VZ,
|
|
"MomentX": MT,
|
|
"MomentY": MFY,
|
|
"MomentZ": MFZ,
|
|
},
|
|
)
|
|
reaction.AppliedLoad.Values += (pointValue,)
|
|
|
|
elif element["geometry_type"] == "Face":
|
|
for iNode, node in enumerate(info["coords"]):
|
|
location = f"({node[0]}, {node[1]}, {node[2]})"
|
|
distance = tuple(info["local_coords"][iNode])
|
|
|
|
NXX = forces["NXX"][iNode]
|
|
NYY = forces["NYY"][iNode]
|
|
NXY = forces["NXY"][iNode]
|
|
MXX = forces["MXX"][iNode]
|
|
MYY = forces["MYY"][iNode]
|
|
MXY = forces["MXY"][iNode]
|
|
|
|
data.append([name, f"LCC-{iCase + 1} @ {distance}", location, NXX, NYY, NXY, MXX, MYY, MXY])
|
|
|
|
pointValue = ifc_file.create_entity(
|
|
"IfcStructuralLoadSingleForce",
|
|
**{
|
|
"Name": "Internal Forces for " + model_cases[iCase]["Name"] + f" @ {distance} on {name}",
|
|
"ForceX": NXX,
|
|
"ForceY": NYY,
|
|
"ForceZ": NXY,
|
|
"MomentX": MXX,
|
|
"MomentY": MYY,
|
|
"MomentZ": MXY,
|
|
},
|
|
)
|
|
reaction.AppliedLoad.Values += (pointValue,)
|
|
|
|
return data
|
|
|
|
|
|
def displacements_to_ifc(ifc_file, ifc_model, result, model_cases, elements):
|
|
result_cases = [dict() for _ in model_cases]
|
|
field_cases = [f"MODEL_DISP[{i}] - {i + 1}" for i in range(len(result_cases))]
|
|
|
|
# Create Result Groups for load case_instance combinations
|
|
for iCase, case_instance in enumerate(model_cases):
|
|
result_cases[iCase]["case_instance"] = ifc_file.create_entity(
|
|
"IfcStructuralResultGroup",
|
|
**{
|
|
"GlobalId": ios.guid.new(),
|
|
"Name": "Global Displacements for " + case_instance["Name"],
|
|
"TheoryType": "FIRST_ORDER_THEORY",
|
|
"ResultForLoadGroup": ifc_file.by_id(case_instance["id"]),
|
|
"IsLinear": True,
|
|
},
|
|
)
|
|
|
|
result_cases[iCase]["assignment"] = ifc_file.create_entity(
|
|
"IfcRelAssignsToGroup",
|
|
**{
|
|
"GlobalId": ios.guid.new(),
|
|
"RelatedObjects": [],
|
|
"RelatingGroup": result_cases[iCase]["case_instance"],
|
|
},
|
|
)
|
|
|
|
if ifc_model.HasResults:
|
|
ifc_model.HasResults += tuple([result["case_instance"] for result in result_cases])
|
|
else:
|
|
ifc_model.HasResults = tuple([result["case_instance"] for result in result_cases])
|
|
|
|
data = []
|
|
for _, element in enumerate(elements):
|
|
group_name = getGroupName(element["ref_id"])
|
|
name = element["Name"]
|
|
info = get_element_data(result, group_name, element)
|
|
assert len(info["coords"]) >= 2
|
|
for iCase, case_instance in enumerate(field_cases):
|
|
displacements = get_element_result_data(
|
|
result, case_instance, group_name, element, field_type="Displacements"
|
|
)
|
|
reaction = ifc_file.create_entity(
|
|
"IfcStructuralCurveReaction" if element["geometry_type"] == "Edge" else "IfcStructuralSurfaceReaction",
|
|
**{
|
|
"GlobalId": ios.guid.new(),
|
|
"Name": "Global Displacements for " + model_cases[iCase]["Name"] + f" on {name}",
|
|
# "AppliedLoad": load["ifcLoad"],
|
|
"GlobalOrLocal": "LOCAL_COORDS",
|
|
"PredefinedType": "DISCRETE",
|
|
},
|
|
)
|
|
result_cases[iCase]["assignment"].RelatedObjects += (reaction,)
|
|
|
|
ifc_file.create_entity(
|
|
"IfcRelConnectsStructuralActivity",
|
|
**{
|
|
"GlobalId": ios.guid.new(),
|
|
"RelatingElement": ifc_file.by_id(element["id"]),
|
|
"RelatedStructuralActivity": reaction,
|
|
},
|
|
)
|
|
|
|
reaction.AppliedLoad = ifc_file.create_entity(
|
|
"IfcStructuralLoadConfiguration",
|
|
**{
|
|
"Name": "Global Displacements for " + model_cases[iCase]["Name"] + f" on {name}",
|
|
"Values": [],
|
|
"Locations": tuple([tuple(node) for node in info["local_coords"]]),
|
|
},
|
|
)
|
|
|
|
if element["geometry_type"] == "Edge":
|
|
for iNode, node in enumerate(info["coords"]):
|
|
location = f"({node[0]}, {node[1]}, {node[2]})"
|
|
distance = info["local_coords"][iNode][0]
|
|
|
|
DX = displacements["DX"][iNode]
|
|
DY = displacements["DY"][iNode]
|
|
DZ = displacements["DZ"][iNode]
|
|
DRX = displacements["DRX"][iNode]
|
|
DRY = displacements["DRY"][iNode]
|
|
DRZ = displacements["DRZ"][iNode]
|
|
|
|
data.append([name, f"LCC-{iCase + 1} @ {distance}", location, DX, DY, DZ, DRX, DRY, DRZ])
|
|
|
|
pointValue = ifc_file.create_entity(
|
|
"IfcStructuralLoadSingleDisplacement",
|
|
**{
|
|
"Name": "Global Displacements for "
|
|
+ model_cases[iCase]["Name"]
|
|
+ f" @ {distance} on {name}",
|
|
"DisplacementX": DX,
|
|
"DisplacementY": DY,
|
|
"DisplacementZ": DZ,
|
|
"RotationalDisplacementRX": DRX,
|
|
"RotationalDisplacementRY": DRY,
|
|
"RotationalDisplacementRZ": DRZ,
|
|
},
|
|
)
|
|
reaction.AppliedLoad.Values += (pointValue,)
|
|
|
|
elif element["geometry_type"] == "Face":
|
|
for iNode, node in enumerate(info["coords"]):
|
|
location = f"({node[0]}, {node[1]}, {node[2]})"
|
|
distance = tuple(info["local_coords"][iNode])
|
|
|
|
DX = displacements["DX"][iNode]
|
|
DY = displacements["DY"][iNode]
|
|
DZ = displacements["DZ"][iNode]
|
|
DRX = displacements["DRX"][iNode]
|
|
DRY = displacements["DRY"][iNode]
|
|
DRZ = displacements["DRZ"][iNode]
|
|
|
|
data.append([name, f"LCC-{iCase + 1} @ {distance}", location, DX, DY, DZ, DRX, DRY, DRZ])
|
|
|
|
pointValue = ifc_file.create_entity(
|
|
"IfcStructuralLoadSingleDisplacement",
|
|
**{
|
|
"Name": "Global Displacements for "
|
|
+ model_cases[iCase]["Name"]
|
|
+ f" @ {distance} on {name}",
|
|
"DisplacementX": DX,
|
|
"DisplacementY": DY,
|
|
"DisplacementZ": DZ,
|
|
"RotationalDisplacementRX": DRX,
|
|
"RotationalDisplacementRY": DRY,
|
|
"RotationalDisplacementRZ": DRZ,
|
|
},
|
|
)
|
|
reaction.AppliedLoad.Values += (pointValue,)
|
|
|
|
return data
|
|
|
|
|
|
def getGroupName(name):
|
|
if "|" in name:
|
|
info = name.split("|")
|
|
sortName = "".join(c for c in info[0] if c.isupper())
|
|
return f"{sortName[2:]}_{info[1]}"
|
|
else:
|
|
return name
|