def import_indexedpolycurve(ipoly, normal, xdir, origin): """ :param ipoly: IFC element :param normal: :param xdir: :param origin: :return: """ from ada import ArcSegment, LineSegment from ada.core.utils import global_2_local_nodes, segments_to_local_points ydir = np.cross(normal, xdir) nodes3d = [p for p in ipoly.Points.CoordList] nodes2d = global_2_local_nodes([xdir, ydir], origin, nodes3d) nodes2d = [np.array([n[0], n[1], 0.0]) for n in nodes2d] seg_list = [] for i, seg in enumerate(ipoly.Segments): if seg.is_a("IfcLineIndex"): v = seg.wrappedValue p1 = nodes2d[v[0] - 1] p2 = nodes2d[v[1] - 1] seg_list.append(LineSegment(p1=p1, p2=p2)) elif seg.is_a("IfcArcIndex"): v = seg.wrappedValue p1 = nodes2d[v[0] - 1] p2 = nodes2d[v[1] - 1] p3 = nodes2d[v[2] - 1] seg_list.append(ArcSegment(p1, p3, midpoint=p2)) else: raise ValueError("Unrecognized type") local_points = [(roundoff(x[0]), roundoff(x[1])) for x in segments_to_local_points(seg_list)] return local_points
def _calc_bbox_of_beam(self) -> Tuple[tuple, tuple]: """Get the bounding box of a beam""" from itertools import chain from ada import Beam, Section from ada.core.utils import roundoff from ..sections import SectionCat bm = self.parent if SectionCat.is_circular_profile( bm.section.type) or SectionCat.is_tubular_profile( bm.section.type): d = bm.section.r * 2 dummy_beam = Beam("dummy", bm.n1.p, bm.n2.p, Section("DummySec", "BG", h=d, w_btn=d, w_top=d)) outer_curve = dummy_beam.get_outer_points() else: outer_curve = bm.get_outer_points() points = np.array(list(chain.from_iterable(outer_curve))) xv = sorted([roundoff(p[0]) for p in points]) yv = sorted([roundoff(p[1]) for p in points]) zv = sorted([roundoff(p[2]) for p in points]) xmin, xmax = xv[0], xv[-1] ymin, ymax = yv[0], yv[-1] zmin, zmax = zv[0], zv[-1] return (xmin, ymin, zmin), (xmax, ymax, zmax)
def test_rotate_about_Z(self): origin = (0, 0, 0) normal = (0, 0, 1) # Start with a 90 degree counter-clockwise rotation (x = pos y) xvec = (0, 1, 0) # Rotate yvec = np.cross(normal, xvec).astype(float) csys2 = np.array([ np.array(xvec).astype(float), yvec, np.array(normal).astype(float) ]) rp2 = np.array(origin) + np.dot( rotation_matrix_csys_rotate(csysA, csys2), pA) assert tuple([roundoff(x) for x in rp2]) == (1.0, -1.0, 0.0) # Rotate another 90 degrees counter-clockwise xvec = (-1, 0, 0) yvec = np.cross(normal, xvec).astype(float) csys3 = np.array([ np.array(xvec).astype(float), yvec, np.array(normal).astype(float) ]) rp3 = np.array(origin) + np.dot( rotation_matrix_csys_rotate(csysA, csys3), pA) assert tuple([roundoff(x) for x in rp3]) == (-1.0, -1.0, 0) rp4 = np.array(origin) + np.dot( rotation_matrix_csys_rotate(csys3, csysA), rp3) assert tuple([roundoff(x) for x in rp4]) == tuple([float(x) for x in pA])
def test_rotate_about_Z(): p_a = (1, 1, 0) # Global axis globx = (1, 0, 0) globy = (0, 1, 0) globz = (0, 0, 1) csys_a = np.array( [np.array(x).astype(float) for x in [globx, globy, globz]]) origin = (0, 0, 0) normal = (0, 0, 1) # Start with a 90 degree counter-clockwise rotation (x = pos y) xvec = (0, 1, 0) # Rotate yvec = np.cross(normal, xvec).astype(float) csys2 = np.array( [np.array(xvec).astype(float), yvec, np.array(normal).astype(float)]) rp2 = np.array(origin) + np.dot(rotation_matrix_csys_rotate(csys_a, csys2), p_a) assert tuple([roundoff(x) for x in rp2]) == (1.0, -1.0, 0.0) # Rotate another 90 degrees counter-clockwise xvec = (-1, 0, 0) yvec = np.cross(normal, xvec).astype(float) csys3 = np.array( [np.array(xvec).astype(float), yvec, np.array(normal).astype(float)]) rp3 = np.array(origin) + np.dot(rotation_matrix_csys_rotate(csys_a, csys3), p_a) assert tuple([roundoff(x) for x in rp3]) == (-1.0, -1.0, 0) rp4 = np.array(origin) + np.dot(rotation_matrix_csys_rotate(csys3, csys_a), rp3) assert tuple([roundoff(x) for x in rp4]) == tuple([float(x) for x in p_a])
def get_morsmel(m): """ MORSMEL Anisotropy, Linear Elastic Structural Analysis, 2-D Membrane Elements and 2-D Thin Shell Elements :param m: :return: """ d = m.groupdict() matno = str_to_int(d["matno"]) return Material( name=mat_names[matno], mat_id=matno, mat_model=CarbonSteel( rho=roundoff(d["rho"]), E=roundoff(d["d11"]), v=roundoff(d["ps1"]), alpha=roundoff(d["alpha1"]), zeta=roundoff(d["damp1"]), sig_p=[], eps_p=[], sig_y=5e6, ), metadata=d, parent=part, )
def get_lcsys(m): d = m.groupdict() return str_to_int(d["transno"]), ( roundoff(d["unix"]), roundoff(d["uniy"]), roundoff(d["uniz"]), )
def test_to_fem(param_models_test_dir): a = build_test_simplestru_fem(use_quads=True) param_model: SimpleStru = a.get_by_name("ParametricModel") assert len(param_model.fem.bcs) == 1 assert len(param_model.fem.elements) == pytest.approx(1584, rel=10) assert len(param_model.fem.nodes) == pytest.approx(5331, rel=80) cog = param_model.fem.elements.calc_cog() tol = 0.01 my_step = a.fem.add_step( ada.fem.StepImplicit("static", total_time=1, max_incr=1, init_incr=1, nl_geom=False)) my_step.add_load(ada.fem.Load("Gravity", "gravity", -9.81)) a.to_fem("SimpleStru_ca", fem_format="code_aster", overwrite=True, execute=False) _ = a.to_ifc(param_models_test_dir / "SimpleStru", return_file_obj=True) assert abs(roundoff(cog.p[0]) - 2.5) < tol assert abs(roundoff(cog.p[1]) - 2.5) < tol assert abs(roundoff(cog.p[2]) - 1.5) < tol assert abs(roundoff(cog.tot_mass) - 6672.406) < tol assert abs(roundoff(cog.tot_vol) - 0.85) < tol
def eval_assertions(section: Section, assertions): props = section.properties for variable, should_be in assertions: calculated = getattr(props, variable) try: assert roundoff(calculated, 10) == roundoff(should_be, 10) except AssertionError as e: raise AssertionError(f"{variable}\n{e}")
def test_calc_cog(self): a = build_test_model() p = a.parts["ParametricModel"] cog = p.fem.elements.calc_cog() tol = 0.01 assert abs(roundoff(cog.p[0]) - 2.5) < tol assert abs(roundoff(cog.p[1]) - 2.5) < tol assert abs(roundoff(cog.p[2]) - 1.5) < tol assert abs(roundoff(cog.tot_mass) - 7854.90) < tol assert abs(roundoff(cog.tot_vol) - 1.001) < tol
def test_calc_cog(self): a = build_test_model() p = a.parts['ParametricModel'] cog = p.fem.elements.calc_cog() tol = 0.01 assert abs(roundoff(cog[0]) - 2.5) < tol assert abs(roundoff(cog[1]) - 2.5) < tol assert abs(roundoff(cog[2]) - 1.5) < tol assert abs(roundoff(cog[3]) - 7854.90) < tol assert abs(roundoff(cog[4]) - 1.001) < tol
def get_flatbar(match, sect_names, fem) -> Section: d = match.groupdict() sec_id = str_to_int(d["geono"]) return Section( name=sect_names[sec_id], sec_id=sec_id, sec_type=Section.TYPES.FLATBAR, h=roundoff(d["hz"]), w_top=roundoff(d["bt"]), w_btn=roundoff(d["bb"]), genprops=GeneralProperties(Sfy=float(d["sfy"]), Sfz=float(d["sfz"])), parent=fem.parent, )
def test_basic_arc2(): origin = (0, 0, 0) xdir = (1, 0, 0) normal = (0, -1, 0) p1 = np.array([-150, 100]) p2 = np.array([-74, 81]) p3 = np.array([-20, 0]) radius = 40 v1 = unit_vector(p2 - p1) v2 = unit_vector(p2 - p3) alpha = angle_between(v1, v2) s = radius / np.sin(alpha / 2) dir_eval = np.cross(v1, v2) if dir_eval < 0: theta = -alpha / 2 else: theta = alpha / 2 A = p2 - v1 * s center = linear_2dtransform_rotate(p2, A, np.rad2deg(theta)) start = intersect_line_circle((p1, p2), center, radius) end = intersect_line_circle((p3, p2), center, radius) vc1 = np.array([start[0], start[1], 0.0]) - np.array([center[0], center[1], 0.0]) vc2 = np.array([end[0], end[1], 0.0]) - np.array([center[0], center[1], 0.0]) arbp = angle_between(vc1, vc2) if dir_eval < 0: gamma = arbp / 2 else: gamma = -arbp / 2 midp = linear_2dtransform_rotate(center, start, np.rad2deg(gamma)) glob_c = local_2_global_points([center], origin, xdir, normal)[0] glob_s = local_2_global_points([start], origin, xdir, normal)[0] glob_e = local_2_global_points([end], origin, xdir, normal)[0] glob_midp = local_2_global_points([midp], origin, xdir, normal)[0] res_center = (-98.7039754, 0.0, 45.94493759) res_start = (-89.00255040102925, 0, 84.75063760025732) res_end = (-65.4219636289688, 0, 68.1329454434532) res_midp = (-75.66203793182973, 0, 78.64156001325857) for r, e in zip(res_start, glob_s): assert roundoff(r, 5) == roundoff(e, 5) for r, e in zip(res_end, glob_e): assert roundoff(r, 4) == roundoff(e, 4) for r, e in zip(res_midp, glob_midp): assert roundoff(r, 4) == roundoff(e, 4) for r, e in zip(res_center, glob_c): assert roundoff(r, 4) == roundoff(e, 4)
def get_beams_within_volume(self, vol_, margins=Settings.point_tol) -> Iterable[Beam]: """ :param vol_: List or tuple of tuples [(xmin, xmax), (ymin, ymax), (zmin, zmax)] :param margins: Add margins to the volume box (equal in all directions). Input is in meters. Can be negative. :return: List of beam ids """ from bisect import bisect_left, bisect_right if margins is not None: vol_new = [] for p in vol_: vol_new.append( (roundoff(p[0] - margins), roundoff(p[1] + margins))) else: vol_new = vol_ vol = vol_new def sort_beams(bms): xkeys = [key[1] for key in bms] xmin = bisect_left(xkeys, vol[0][0]) xmax = bisect_right(xkeys, vol[0][1]) within_x_list = sorted(bms[xmin:xmax], key=lambda elem: elem[2]) ykeys = [key[2] for key in within_x_list] ymin = bisect_left(ykeys, vol[1][0]) ymax = bisect_right(ykeys, vol[1][1]) within_y_list = sorted(within_x_list[ymin:ymax], key=lambda elem: elem[3]) zkeys = [key[3] for key in within_y_list] zmin = bisect_left(zkeys, vol[2][0]) zmax = bisect_right(zkeys, vol[2][1]) within_vol_list = within_y_list[zmin:zmax] return [bm[0] for bm in within_vol_list] bm_list1 = [(bm.name, bm.n1.x, bm.n1.y, bm.n1.z) for bm in sorted(self._beams, key=lambda bm: bm.n1.x)] bm_list2 = [(bm.name, bm.n2.x, bm.n2.y, bm.n2.z) for bm in sorted(self._beams, key=lambda bm: bm.n2.x)] return set([ self._dmap[bm_id] for bms_ in (bm_list1, bm_list2) for bm_id in sort_beams(bms_) ])
def test_basic_arc_rot2(): p1 = (0, -5) p2 = (0, 0) p3 = (5, 0) radius = 0.2 center, start, end, midp = calc_2darc_start_end_from_lines_radius(p1, p2, p3, radius) rcenter, _rradius = calc_arc_radius_center_from_3points(start, midp, end) v1 = (start, np.array(p1)) v2 = (np.array(p3), end) rp = intersection_point(v1, v2) assert roundoff(rp[0]) == roundoff(p2[0]) assert roundoff(rp[1]) == roundoff(p2[1])
def get_nodes_and_elements(gmsh_session, fem, fem_set_name="all_elements"): nodes = list(gmsh_session.model.mesh.getNodes(-1, -1)) # Get nodes fem._nodes = Nodes( [ Node( [roundoff(x) for x in gmsh_session.model.mesh.getNode(n)[0]], n, parent=fem, ) for n in nodes[0] ], parent=fem, ) # Get elements elemTypes, elemTags, elemNodeTags = gmsh_session.model.mesh.getElements( 2, -1) elements = [] for k, element_list in enumerate(elemTags): face, dim, morder, numv, parv, _ = gmsh_session.model.mesh.getElementProperties( elemTypes[k]) elem_type = gmsh_map[face] for j, eltag in enumerate(element_list): nodes = [] for i in range(numv): idtag = numv * j + i p1 = elemNodeTags[k][idtag] nodes.append(fem.nodes.from_id(p1)) el = Elem(eltag, nodes, elem_type, parent=fem) elements.append(el) fem._elements = FemElements(elements, fem_obj=fem) femset = FemSet(fem_set_name, elements, "elset") fem.sets.add(femset)
def p_roundoff(self, scale_factor: Union[int, float] = 1, precision: int = Settings.precision) -> None: from ada.core.utils import roundoff self.p = np.array( [roundoff(scale_factor * x, precision=precision) for x in self.p])
def get_gpipe(match): d = match.groupdict() sec_id = str_to_int(d["geono"]) if sec_id not in sect_names: sec_name = f"TUB{sec_id}" else: sec_name = sect_names[sec_id] t = d["t"] if d["t"] is not None else (d["dy"] - d["di"]) / 2 return Section( name=sec_name, sec_id=sec_id, sec_type="TUB", r=roundoff(float(d["dy"]) / 2), wt=roundoff(t), genprops=GeneralProperties(sfy=float(d["sfy"]), sfz=float(d["sfz"])), parent=fem.parent, )
def get_tubular_section(match, sect_names, fem) -> Section: d = match.groupdict() sec_id = str_to_int(d["geono"]) if sec_id not in sect_names: sec_name = f"TUB{sec_id}" else: sec_name = sect_names[sec_id] t = d["t"] if d["t"] is not None else (d["dy"] - d["di"]) / 2 return Section( name=sec_name, sec_id=sec_id, sec_type=Section.TYPES.TUBULAR, r=roundoff(float(d["dy"]) / 2), wt=roundoff(t), genprops=GeneralProperties(Sfy=float(d["sfy"]), Sfz=float(d["sfz"])), parent=fem.parent, )
def write_mat(m: Material): return " MISOIEP{:>11}{:>10.3E}{:>12}{:>10.3E}{:>13}{:>11}".format( m.id, m.model.E, m.model.v, float(m.model.sig_y), roundoff(m.model.rho), m.model.alpha, )
def make_box_by_points(p1, p2, scale=1.0): from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeBox from OCC.Core.gp import gp_Pnt if type(p1) == list or type(p1) == tuple or type(p1) is np.ndarray: deltas = [roundoff((p2_ - p1_) * scale) for p1_, p2_ in zip(p1, p2)] p1_in = [roundoff(x * scale) for x in p1] else: raise ValueError("Unknown input format {type(p1)}") dx = deltas[0] dy = deltas[1] dz = deltas[2] gp = gp_Pnt(p1_in[0], p1_in[1], p1_in[2]) return BRepPrimAPI_MakeBox(gp, dx, dy, dz).Shape()
def get_mesh_nodes(self, e): """ :param e: :return: """ nco = gmsh.model.mesh.getNode(e) return Node([roundoff(x) for x in nco[0]], e, parent=self._part.fem)
def pipe_bend_radius(self): wt = self.section.wt r = self.section.r d = r * 2 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 return roundoff(d + corr_t / 2.0)
def create_property_set(name, ifc_file, metadata_props): owner_history = ifc_file.by_type("IfcOwnerHistory")[0] properties = [] def ifc_value(v): return ifc_file.create_entity("IfcText", str(v)) def to_str(in_enum): return ( f"{in_enum}".replace("(", "") .replace(")", "") .replace(" ", "") .replace(",", ";") .replace("[", "") .replace("]", "") ) for key, value in metadata_props.items(): if type(value) in (tuple, list): if type(value[0]) in (list, tuple, np.ndarray): for i, v in enumerate(value): if type(v) is np.ndarray: v = [roundoff(x) for x in v] properties.append( ifc_file.create_entity( "IfcPropertySingleValue", Name=f"{key}_{i}", NominalValue=ifc_value(to_str(v)), ) ) else: properties.append( ifc_file.create_entity( "IfcPropertySingleValue", Name=key, NominalValue=ifc_value(to_str(value)), ) ) else: properties.append( ifc_file.create_entity( "IfcPropertySingleValue", Name=key, NominalValue=ifc_value(value), ) ) atts = { "GlobalId": ifcopenshell.guid.new(), "OwnerHistory": owner_history, "Name": name, "HasProperties": properties, } return ifc_file.create_entity("IfcPropertySet", **atts)
def grab_mass(match): d = match.groupdict() nodeno = str_to_int(d["nodeno"]) mass_in = [ roundoff(d["m1"]), roundoff(d["m2"]), roundoff(d["m3"]), roundoff(d["m4"]), roundoff(d["m5"]), roundoff(d["m6"]), ] masses = [m for m in mass_in if m != 0.0] if checkEqual2(masses): mass_type = None masses = [masses[0]] if len(masses) > 0 else [0.0] else: mass_type = "anisotropic" no = fem.nodes.from_id(nodeno) fem_set = FemSet(f"m{nodeno}", [], "elset", metadata=dict(internal=True), parent=fem) mass = Mass(f"m{nodeno}", fem_set, masses, "mass", ptype=mass_type, parent=fem) elem = Elem(no.id, [no], "mass", fem_set, mass_props=mass, parent=fem) fem.elements.add(elem) fem_set.add_members([elem]) fem.sets.add(fem_set) return Mass(f"m{nodeno}", fem_set, masses, "mass", ptype=mass_type, parent=fem)
def _init_orientation(self, angle=None, up=None) -> None: xvec = unit_vector(self.n2.p - self.n1.p) tol = 1e-3 zvec = calc_zvec(xvec) gup = np.array(zvec) if up is None: if angle != 0.0 and angle is not None: from pyquaternion import Quaternion my_quaternion = Quaternion(axis=xvec, degrees=angle) rot_mat = my_quaternion.rotation_matrix up = np.array([ roundoff(x) if abs(x) != 0.0 else 0.0 for x in np.matmul(gup, np.transpose(rot_mat)) ]) else: up = np.array( [roundoff(x) if abs(x) != 0.0 else 0.0 for x in gup]) yvec = calc_yvec(xvec, up) else: if (len(up) == 3) is False: raise ValueError("Up vector must be length 3") if vector_length(xvec - up) < tol: raise ValueError( "The assigned up vector is too close to your beam direction" ) yvec = calc_yvec(xvec, up) # TODO: Fix improper calculation of angle (e.g. xvec = [1,0,0] and up = [0,1,0] should be 270? rad = angle_between(up, yvec) angle = np.rad2deg(rad) up = np.array(up) # lup = np.cross(xvec, yvec) self._xvec = xvec self._yvec = np.array([roundoff(x) for x in yvec]) self._up = up self._angle = angle
def __init__(self, name, points2d, origin, xdir, normal, rev_angle, tol=1e-3, **kwargs): self._name = name poly = CurvePoly( points2d=points2d, normal=[roundoff(x) for x in normal], origin=origin, xdir=[roundoff(x) for x in xdir], tol=tol, parent=self, ) self._poly = poly self._revolve_angle = rev_angle self._revolve_axis = [roundoff(x) for x in poly.ydir] self._revolve_origin = origin super(PrimRevolve, self).__init__( name, self._poly.make_revolve_solid( self._revolve_axis, self._revolve_angle, self._revolve_origin, ), **kwargs, )
def test_roundtrip_global_coords_2_local(self): # Local Coordinate System xvec = (1, 0, 0) yvec = (0, 0, 1) normal = np.cross(xvec, yvec) csys2 = [xvec, yvec] origin = (0, 0, 0) point = (2, -0.3, 2) loc_points = global_2_local_nodes(csys2, origin, [point]) glob_points = local_2_global_nodes(loc_points, origin, xvec, normal) ev1 = tuple([roundoff(x) for x in glob_points[0]]) ev2 = tuple([float(x) for x in point]) assert ev1 == ev2
def test_basic_arc(): p1 = (0, 5) p2 = (0, 0) p3 = (5, 0) radius = 0.2 center, start, end, midp = calc_2darc_start_end_from_lines_radius(p1, p2, p3, radius) rcenter, rradius = calc_arc_radius_center_from_3points(start, midp, end) v1 = (start, np.array(p1)) v2 = (np.array(p3), end) rp = [roundoff(x) for x in intersection_point(v1, v2)] assert rp[0] == p2[0] assert rp[1] == p2[1] assert radius == rradius
def read_line_section(elem: Elem, fem: FEM, mat: Material, geono, d, lcsysd, hinges_global, eccentricities): transno = str_to_int(d["transno"]) sec = fem.parent.sections.get_by_id(geono) n1, n2 = elem.nodes v = n2.p - n1.p if vector_length(v) == 0.0: xvec = [1, 0, 0] else: xvec = unit_vector(v) zvec = lcsysd[transno] crossed = np.cross(zvec, xvec) ma = max(abs(crossed)) yvec = tuple([roundoff(x / ma, 3) for x in crossed]) fix_data = str_to_int(d["fixno"]) ecc_data = str_to_int(d["eccno"]) members = None if d["members"] is not None: members = [ str_to_int(x) for x in d["members"].replace("\n", " ").split() ] if fix_data == -1: add_hinge_prop_to_elem(elem, members, hinges_global, xvec, yvec) if ecc_data == -1: add_ecc_to_elem(elem, members, eccentricities, fix_data) fem_set = FemSet(sec.name, [elem], "elset", metadata=dict(internal=True), parent=fem) fem.sets.add(fem_set, append_suffix_on_exist=True) fem_sec = FemSection( sec_id=geono, name=sec.name, sec_type=ElemType.LINE, elset=fem_set, section=sec, local_z=zvec, local_y=yvec, material=mat, parent=fem, ) return fem_sec
def get_nodes_and_elements(gmsh, fem=None, fem_set_name="all_elements"): """ :param gmsh: :type gmsh: gmsh :param fem: :type fem: ada.fem.FEM :param fem_set_name: :type fem_set_name: str """ from ada.fem import FEM fem = FEM("AdaFEM") if fem is None else fem nodes = list(gmsh.model.mesh.getNodes(-1, -1)) # Get nodes fem._nodes = Nodes( [ Node( [roundoff(x) for x in gmsh.model.mesh.getNode(n)[0]], n, parent=fem, ) for n in nodes[0] ], parent=fem, ) # Get elements elemTypes, elemTags, elemNodeTags = gmsh.model.mesh.getElements(2, -1) elements = [] for k, element_list in enumerate(elemTags): face, dim, morder, numv, parv, _ = gmsh.model.mesh.getElementProperties( elemTypes[k]) elem_type = gmsh_map[face] for j, eltag in enumerate(element_list): nodes = [] for i in range(numv): idtag = numv * j + i p1 = elemNodeTags[k][idtag] nodes.append(fem.nodes.from_id(p1)) el = Elem(eltag, nodes, elem_type, parent=fem) elements.append(el) fem._elements = FemElements(elements, fem_obj=fem) femset = FemSet(fem_set_name, elements, "elset") fem.sets.add(femset)