Skip to content

Commit

Permalink
Bugfixes and formatting
Browse files Browse the repository at this point in the history
Fixed error in COG calculation causing test_calc_cog to fail.

specify length and angle tol in build_pipe()

Freeze pythonocc-core and occt version
  • Loading branch information
Krande committed May 26, 2021
1 parent e1fd8df commit bf0178f
Show file tree
Hide file tree
Showing 15 changed files with 188 additions and 163 deletions.
3 changes: 2 additions & 1 deletion environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ name: adaenv
channels:
- conda-forge
dependencies:
- pythonocc-core
- pythonocc-core==7.5.1
- occt==7.5.1
- ifcopenshell
3 changes: 2 additions & 1 deletion images/environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ channels:
- conda-forge
dependencies:
- python==3.8.8
- pythonocc-core
- pythonocc-core==7.5.1
- occt==7.5.1
- ifcopenshell
- jupyter
67 changes: 42 additions & 25 deletions src/ada/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
"Beam",
"Plate",
"Pipe",
"PipeSegStraight",
"PipeSegElbow",
"Wall",
"Penetration",
"Section",
Expand Down Expand Up @@ -2598,7 +2600,7 @@ def _build_pipe(self):
:return:
"""
from ada.core.utils import local_2_global_nodes, make_arc_segment
from ada.core.utils import make_arc_segment

segs = []
for p1, p2 in zip(self.points[:-1], self.points[1:]):
Expand All @@ -2612,18 +2614,23 @@ def _build_pipe(self):

# Make elbows and adjust segments
props = dict(section=self.section, material=self.material, parent=self, units=self.units)

angle_tol = 1e-1
len_tol = _Settings.point_tol if self.units == "m" else _Settings.point_tol * 1000
for i, (seg1, seg2) in enumerate(zip(segments[:-1], segments[1:])):
p11, p12 = seg1
p21, p22 = seg2
vlen1 = vector_length(seg1[1].p - seg1[0].p)
vlen2 = vector_length(seg2[1].p - seg2[0].p)
if vlen1 < _Settings.point_tol or vlen2 == _Settings.point_tol:
logging.error("Segment Length is zero. Skipping")

if vlen1 < len_tol or vlen2 == len_tol:
logging.error(f'Segment Length is below point tolerance for unit "{self.units}". Skipping')
continue
xvec1 = p12.p - p11.p
xvec2 = p22.p - p21.p
if angle_between(xvec1, xvec2) in (np.pi, 0):
xvec1 = unit_vector(p12.p - p11.p)
xvec2 = unit_vector(p22.p - p21.p)
a = angle_between(xvec1, xvec2)
res = True if abs(abs(a) - abs(np.pi)) < angle_tol or abs(abs(a) - 0.0) < angle_tol else False

if res is True:
self._segments.append(PipeSegStraight(next(seg_names), p11, p12, **props))
else:
if p12 != p21:
Expand All @@ -2644,26 +2651,34 @@ def _build_pipe(self):
continue

if i == 0 or len(self._segments) == 0:
self._segments.append(PipeSegStraight(next(seg_names), Node(seg1.p1), Node(seg1.p2), **props))
self._segments.append(
PipeSegStraight(
next(seg_names), Node(seg1.p1, units=self.units), Node(seg1.p2, units=self.units), **props
)
)
else:
if len(self._segments) == 0:
print("sd")
continue
pseg = self._segments[-1]
pseg.p2 = Node(seg1.p2)
pseg.p2 = Node(seg1.p2, units=self.units)

self._segments.append(
PipeSegElbow(
next(seg_names) + "_Elbow",
Node(seg1.p1),
Node(p21.p),
Node(seg2.p2),
Node(seg1.p1, units=self.units),
Node(p21.p, units=self.units),
Node(seg2.p2, units=self.units),
arc.radius,
**props,
arc_seg=arc,
)
)
self._segments.append(PipeSegStraight(next(seg_names), Node(seg2.p1), Node(seg2.p2), **props))
self._segments.append(
PipeSegStraight(
next(seg_names), Node(seg2.p1, units=self.units), Node(seg2.p2, units=self.units), **props
)
)

@property
def segments(self):
Expand Down Expand Up @@ -2710,8 +2725,8 @@ def pipe_bend_radius(self):
wt = self.section.wt
r = self.section.r
d = r * 2
w_tol = 0.125
cor_tol = 0.003
w_tol = 0.125 if self.units == "m" else 125
cor_tol = 0.003 if self.units == "m" else 3
corr_t = (wt - (wt * w_tol)) - cor_tol
d -= 2.0 * corr_t

Expand Down Expand Up @@ -2745,14 +2760,15 @@ def units(self, value):
self.n2.units = value
self.section.units = value
self.material.units = value
self._segments = []
for p in self.points:
p.units = value
self._build_pipe()
self._units = value

def _generate_ifc_pipe(self):
from ada.core.ifc_utils import create_ifclocalplacement, create_property_set
from ada.core.constants import X, Z
from ada.core.ifc_utils import create_ifclocalplacement, create_property_set

if self.parent is None:
raise ValueError("Cannot build ifc element without parent")
Expand Down Expand Up @@ -2882,12 +2898,12 @@ def ifc_elem(self):
return self._ifc_elem

def _to_ifc_elem(self):
from ada.core.constants import O, X, Z
from ada.core.ifc_utils import ( # create_ifcrevolveareasolid,
create_ifcaxis2placement,
create_ifcpolyline,
to_real,
)
from ada.core.constants import X, Y, Z, O

if self.parent is None:
raise ValueError("Parent cannot be None for IFC export")
Expand Down Expand Up @@ -2999,7 +3015,7 @@ def xvec2(self):

@property
def geom(self):
from ada.core.utils import sweep_pipe, make_edges_and_fillet_from_3points
from ada.core.utils import make_edges_and_fillet_from_3points, sweep_pipe

i = self.parent._segments.index(self)
if i != 0:
Expand Down Expand Up @@ -3033,7 +3049,7 @@ def ifc_elem(self):

def _elbow_tesselated(self, f, schema, a, context):
import ifcopenshell.geom
from ada.core.ifc_utils import create_ifcpolyline, to_real

shape = self.geom
if shape is None:
logging.error(f"Unable to create geometry for Branch {self.name}")
Expand All @@ -3057,9 +3073,12 @@ def _elbow_tesselated(self, f, schema, a, context):
return ifc_shape

def _elbow_revolved_solid(self, f, context):
from ada.core.ifc_utils import create_ifcrevolveareasolid, create_ifcaxis2placement
from ada.core.utils import normal_to_points_in_plane, get_center_from_3_points_and_radius
from ada.core.constants import X, Y, Z, O
from ada.core.constants import O, X, Z
from ada.core.ifc_utils import create_ifcaxis2placement
from ada.core.utils import (
get_center_from_3_points_and_radius,
normal_to_points_in_plane,
)

center, _, _, _ = get_center_from_3_points_and_radius(self.p1.p, self.p2.p, self.p3.p, self.bend_radius)

Expand All @@ -3085,8 +3104,6 @@ def _elbow_revolved_solid(self, f, context):

def _to_ifc_elem(self):
from ada.core.ifc_utils import create_ifclocalplacement
from ada.core.utils import faceted_tol
from ada.core.constants import X, Y, Z, O

if self.parent is None:
raise ValueError("Parent cannot be None for IFC export")
Expand All @@ -3107,7 +3124,7 @@ def _to_ifc_elem(self):
ifc_elbow = self._elbow_revolved_solid(f, context)

pfitting_placement = create_ifclocalplacement(f)
# pfitting = f.createIfcBuildingElementProxy(

pfitting = f.createIfcPipeFitting(
create_guid(),
owner_history,
Expand Down
17 changes: 8 additions & 9 deletions src/ada/base/renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ def DisplayAdaPart(self, part):
)

def DisplayObj(self, obj):
from ada import Beam, Part, Plate, Shape, Pipe
from ada import Beam, Part, Pipe, Plate, Shape

if issubclass(type(obj), Part) is True:
self.DisplayAdaPart(obj)
Expand Down Expand Up @@ -690,14 +690,13 @@ def write_metadata_to_html(met_d):
fem_data=", ".join([f"(<b>{x}</b>: {y})" for x, y in fem_data.items()])
)
vol_cog_str = ", ".join([f"{x:.3f}" for x in selected_part.fem.nodes.vol_cog])
res = selected_part.fem.elements.calc_cog()
cogx, cogy, cogz, tot_mass, tot_vol, sh_mass, bm_mass, no_mass = res
cog_str = ", ".join([f"{x:.3f}" for x in (cogx, cogy, cogz)])
html_value += f"<b>Vol:</b> {tot_vol:.3f} <b>COG:</b> ({vol_cog_str}) <br>"
html_value += f"<b>Mass:</b> {tot_mass:.1f} <b>COG:</b> ({cog_str}) <br>"
html_value += f"<b>Beam mass:</b> {bm_mass:.1f}<br>"
html_value += f"<b>Shell mass:</b> {sh_mass:.1f}<br>"
html_value += f"<b>Node mass:</b> {no_mass:.1f}<br>"
cog = selected_part.fem.elements.calc_cog()
cog_str = ", ".join([f"{x:.3f}" for x in cog.p])
html_value += f"<b>Vol:</b> {cog.tot_vol:.3f} <b>COG:</b> ({vol_cog_str}) <br>"
html_value += f"<b>Mass:</b> {cog.tot_mass:.1f} <b>COG:</b> ({cog_str}) <br>"
html_value += f"<b>Beam mass:</b> {cog.bm_mass:.1f}<br>"
html_value += f"<b>Shell mass:</b> {cog.sh_mass:.1f}<br>"
html_value += f"<b>Node mass:</b> {cog.no_mass:.1f}<br>"
html_value += (
"<br><br>Note! Mass calculations are calculated based on <br>beam offsets "
"(which is not shown in the viewer yet)."
Expand Down
4 changes: 2 additions & 2 deletions src/ada/core/ifc_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import uuid

from ifcopenshell import main
from ifcopenshell.file import file
from ifcopenshell.file import file as ifc_file
from ifcopenshell.guid import compress

# A quick way to setup an 'empty' IFC file, taken from:
Expand Down Expand Up @@ -72,4 +72,4 @@ def _():

d.update(dict(_()))

return file.from_string(TEMPLATE % d)
return ifc_file.from_string(TEMPLATE % d)
18 changes: 15 additions & 3 deletions src/ada/core/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -565,15 +565,27 @@ def intersect_line_circle(line, center, radius):


def get_center_from_3_points_and_radius(p1, p2, p3, radius):
from ada.core.constants import X, Y, Z
"""
:param p1:
:param p2:
:param p3:
:param radius:
:return:
"""
p1 = np.array(p1)
p2 = np.array(p2)
p3 = np.array(p3)
from ada.core.constants import X, Y

points = [p1, p2, p3]
n = normal_to_points_in_plane(points)
xv = p2 - p1
yv = calc_yvec(xv, n)
if angle_between(xv, X) in (np.pi, 0) and angle_between(yv, Y) in (np.pi, 0):
locn = [p-p1 for p in points]
locn = [p - p1 for p in points]
res_locn = calc_2darc_start_end_from_lines_radius(*locn, radius)
res_glob = [np.array([p[0], p[1], 0])+p1 for p in res_locn]
res_glob = [np.array([p[0], p[1], 0]) + p1 for p in res_locn]
else:
locn = global_2_local_nodes([xv, yv], p1, points)
res_loc = calc_2darc_start_end_from_lines_radius(*locn, radius)
Expand Down
44 changes: 23 additions & 21 deletions src/ada/fem/containers.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
from bisect import bisect_left
from dataclasses import dataclass
from itertools import chain, groupby
from operator import attrgetter

import numpy as np


@dataclass
class COG:
p: np.array
tot_mass: float
tot_vol: float
sh_mass: float
bm_mass: float
no_mass: float


class FemElements:
"""
Expand Down Expand Up @@ -179,6 +190,7 @@ def calc_cog(self):
nodes
:return: cogx, cogy, cogz, tot_mass, tot_vol
:rtype: COG
"""
from itertools import chain

Expand Down Expand Up @@ -207,12 +219,9 @@ def calc_sh_elem(el):
area = poly_area(x, y)
vol_ = t * area
mass = vol_ * el.fem_sec.material.model.rho
mass_per_node = mass / len(el.nodes)
center = sum([e.p for e in el.nodes]) / len(el.nodes)

# Have not added offset to fem_section yet
# adjusted_nodes = [e.p+t*normal for e in el.nodes]

return mass_per_node, [e for e in el.nodes], vol_
return mass, center, vol_

def calc_bm_elem(el):
"""
Expand All @@ -225,9 +234,9 @@ def calc_bm_elem(el):
elem_len = vector_length(nodes_[-1] - nodes_[0])
vol_ = el.fem_sec.section.properties.Ax * elem_len
mass = vol_ * el.fem_sec.material.model.rho
mass_per_node = mass / 2
center = sum([e.p for e in el.nodes]) / len(el.nodes)

return mass_per_node, [el.nodes[0], el.nodes[-1]], vol_
return mass, center, vol_

def calc_mass_elem(el):
"""
Expand All @@ -238,36 +247,29 @@ def calc_mass_elem(el):
if el.mass_props.type != "MASS":
raise NotImplementedError(f'Mass type "{el.mass_props.type}" is not yet implemented')
mass = el.mass_props.mass
nodes_ = el.nodes
vol_ = 0.0
return mass, nodes_, vol_
return mass, el.nodes[0].p, vol_

sh = list(chain(map(calc_sh_elem, self.shell)))
bm = list(chain(map(calc_bm_elem, self.beams)))
ma = list(chain(map(calc_mass_elem, self.masses)))
mcogx = 0.0
mcogy = 0.0
mcogz = 0.0

tot_mass = 0.0
tot_vol = 0.0
mcog_ = np.array([0, 0, 0]).astype(float)

sh_mass = sum([r[0] for r in sh])
bm_mass = sum([r[0] for r in bm])
no_mass = sum([r[0] for r in ma])

for m, nodes, vol in sh + bm + ma:
for m, c, vol in sh + bm + ma:
tot_vol += vol
tot_mass += m
for n in nodes:
mcogx += m * n[0]
mcogy += m * n[1]
mcogz += m * n[2]
mcog_ += m * np.array(c).astype(float)

cogx = mcogx / tot_mass
cogy = mcogy / tot_mass
cogz = mcogz / tot_mass
cog_ = mcog_ / tot_mass

return cogx, cogy, cogz, tot_mass, tot_vol, sh_mass, bm_mass, no_mass
return COG(cog_, tot_mass, tot_vol, sh_mass, bm_mass, no_mass)

@property
def max_el_id(self):
Expand Down
Loading

0 comments on commit bf0178f

Please sign in to comment.