def generate_ifc_cylinder_geom(shape: PrimCyl, f): """Create IfcExtrudedAreaSolid from primitive PrimCyl""" p1 = shape.p1 p2 = shape.p2 r = shape.r vec = np.array(p2) - np.array(p1) uvec = unit_vector(vec) vecdir = to_real(uvec) cr_dir = np.array([0, 0, 1]) if vector_length(abs(uvec) - abs(cr_dir)) == 0.0: cr_dir = np.array([1, 0, 0]) perp_dir = np.cross(uvec, cr_dir) if vector_length(perp_dir) == 0.0: raise ValueError("Perpendicular dir cannot be zero") create_ifc_placement(f, to_real(p1), vecdir, to_real(perp_dir)) opening_axis_placement = create_ifc_placement(f, to_real(p1), vecdir, to_real(perp_dir)) depth = vector_length(vec) profile = f.createIfcCircleProfileDef("AREA", shape.name, None, r) return create_ifcextrudedareasolid(f, profile, opening_axis_placement, Z, depth)
def calc_con_points(self, point_tol=Settings.point_tol): from ada.core.vector_utils import sort_points_by_dist a = self.n1.p b = self.n2.p points = [tuple(con.centre) for con in self.connected_to] def is_mem_eccentric(mem, centre): is_ecc = False end = None if point_tol < vector_length(mem.n1.p - centre) < mem.length * 0.9: is_ecc = True end = mem.n1.p if point_tol < vector_length(mem.n2.p - centre) < mem.length * 0.9: is_ecc = True end = mem.n2.p return is_ecc, end if len(self.connected_to) == 1: con = self.connected_to[0] if con.main_mem == self: for m in con.beams: if m != self: is_ecc, end = is_mem_eccentric(m, con.centre) if is_ecc: logging.info(f'do something with end "{end}"') points.append(tuple(end)) midpoints = [] prev_p = None for p in sort_points_by_dist(a, points): p = np.array(p) bmlen = self.length vlena = vector_length(p - a) vlenb = vector_length(p - b) if prev_p is not None: if vector_length(p - prev_p) < point_tol: continue if vlena < point_tol: self._connected_end1 = self.connected_to[points.index( tuple(p))] prev_p = p continue if vlenb < point_tol: self._connected_end2 = self.connected_to[points.index( tuple(p))] prev_p = p continue if vlena > bmlen or vlenb > bmlen: prev_p = p continue midpoints += [p] prev_p = p return midpoints
def is_mem_eccentric(mem, centre): is_ecc = False end = None if point_tol < vector_length(mem.n1.p - centre) < mem.length * 0.9: is_ecc = True end = mem.n1.p if point_tol < vector_length(mem.n2.p - centre) < mem.length * 0.9: is_ecc = True end = mem.n2.p return is_ecc, end
def test_3d_input(dummy_display): pl1 = ada.Plate("MyPl", [(0, 0, 0), (5, 0, 0), (5, 5, 0), (0, 5, 0)], 20e-3, use3dnodes=True) origin = np.array([0, 0, 0]) assert vector_length(pl1.placement.origin - origin) < 1e-8 assert vector_length(pl1.placement.zdir - np.array([0, 0, 1])) < 1e-8 assert vector_length(pl1.poly.nodes[0].p - origin) < 1e-8 assert_points = [(0.0, 0.0), (5.0, 0.0), (5.0, -5.0), (0.0, -5.0)] for i, (x, y) in enumerate(pl1.poly.points2d): x_, y_ = assert_points[i] assert x_ == x and y_ == y dummy_display(pl1)
def import_straight_beam(ifc_elem, axis, name, sec, mat, ifc_ref: IfcRef, assembly: Assembly) -> Beam: p1_loc = axis.Points[0].Coordinates p2_loc = axis.Points[1].Coordinates ifc_axis_2_place3d = ifc_elem.ObjectPlacement.RelativePlacement origin = ifc_axis_2_place3d.Location.Coordinates local_z = np.array(ifc_axis_2_place3d.Axis.DirectionRatios) local_x = np.array(ifc_axis_2_place3d.RefDirection.DirectionRatios) local_y = calc_yvec(local_x, local_z) # res = transform3d([local_x, local_y, local_z], [X, Y], origin, [p1_loc, p2_loc]) vlen = vector_length(np.array(p2_loc) - np.array(p1_loc)) p1 = origin p2 = np.array(p1) + local_z * vlen return Beam(name, p1, p2, sec=sec, mat=mat, up=local_y, guid=ifc_elem.GlobalId, ifc_ref=ifc_ref, units=assembly.units)
def add(self, node: Node, point_tol: float = Settings.point_tol, allow_coincident: bool = False) -> Node: """Insert node into sorted list""" def insert_node(n, i): new_id = int(self._maxid + 1) if len(self._nodes) > 0 else 1 if n.id in self._idmap.keys() or n.id is None: n.id = new_id self._nodes.insert(i, n) self._idmap[n.id] = n self._bbox = None self._maxid = n.id if n.id > self._maxid else self._maxid index = bisect_left(self._nodes, node) if (len(self._nodes) != 0) and allow_coincident is False: res = self.get_by_volume(node.p, tol=point_tol) if len(res) == 1: nearest_node = res[0] vlen = vector_length(nearest_node.p - node.p) if vlen < point_tol: logging.debug( f'Replaced new node with node id "{nearest_node.id}" found within point tolerances' ) return nearest_node insert_node(node, index) if node.parent is None: node.parent = self.parent return node
def test_floaty_input_ex1(): origin = [362237.0037951513, 100000.0, 560985.9095763591] csys = [ [0.0, -1.0, 0.0], [-0.4626617625735456, 0.0, 0.8865348799975894], [-0.8865348799975894, 0.0, -0.4626617625735456], ] local_points2d = [ [4.363213751783499e-11, 229.80445306040926], [1.4557793281525672e-11, -57.217605078163196], [2.912511939794014e-11, -207.22885580839431], [-330.0, -207.25, -4.518217213237464e-11], [-400.0, -122.2, 50.0], [-400.0, 42.79, 50.0], [-325.0, 267.83, 50.0], [-85.00004587126028, 650.0198678083951, 24.999999999931404], [-35.0, 650.02, -7.881391015070461e-12], [-35.0, 350.1], [-15.14, 261.52], ] thick = 30 pl = ada.Plate( "test", local_points2d, thick, placement=ada.Placement(origin=origin, zdir=csys[2], xdir=csys[0]), units="mm" ) assert tuple(pl.placement.origin) == tuple(origin) assert tuple(pl.placement.zdir) == tuple(csys[2]) assert vector_length(pl.nodes[0].p - np.array([362130.68206185, 100000.0, 561189.63923958])) < 1e-8
def add_geom(self, geom, obj, geom_repr=None): from ada.concepts.transforms import Placement from ada.core.vector_utils import vector_length from .utils import transform_shape name = obj.name if obj.name is not None else next(shp_names) Interface_Static_SetCVal("write.step.product.name", name) # Transform geometry res = obj.placement.absolute_placement() if vector_length(res - Placement().origin) > 0: geom = transform_shape(geom, transform=tuple(res)) try: if geom_repr == ElemType.SHELL: stat = self.writer.Transfer( geom, STEPControl_ShellBasedSurfaceModel) else: stat = self.writer.Transfer(geom, STEPControl_AsIs) except BaseException as e: logging.info(f"Passing {obj} due to {e}") return None if int(stat) > int(IFSelect_RetError): raise Exception("Some Error occurred") item = stepconstruct_FindEntity(self.fp, geom) if not item: logging.debug("STEP item not found for FindEntity") else: item.SetName(TCollection_HAsciiString(name))
def __eq__(self, other: Placement): from ada.core.vector_utils import vector_length for prop in ["origin", "xdir", "ydir", "zdir"]: if vector_length(getattr(other, prop) - getattr(self, prop)) > 0.0: return False return True
def calc_bm_elem(el: Elem): nodes_ = el.get_offset_coords() 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 center = sum([e.p for e in el.nodes]) / len(el.nodes) return mass, center, vol_
def length(self) -> float: """Returns the length of the beam""" p1 = self.n1.p p2 = self.n2.p if self.e1 is not None: p1 += self.e1 if self.e2 is not None: p2 += self.e2 return vector_length(p2 - p1)
def get_bm_sections(model: gmsh.model, beam: Beam, gmsh_data, fem: FEM): from ada.core.vector_utils import vector_length tags = [] for dim, ent in gmsh_data.entities: _, tag, _ = model.mesh.getElements(1, ent) tags += tag elements = [ fem.elements.from_id(elid) for elid in chain.from_iterable(tags) ] set_name = make_name_fem_ready(f"el{beam.name}_set_bm") fem_sec_name = make_name_fem_ready(f"d{beam.name}_sec_bm") fem_set = FemSet(set_name, elements, FemSet.TYPES.ELSET, parent=fem) fem_sec = FemSection(fem_sec_name, ElemType.LINE, fem_set, beam.material, beam.section, beam.ori[2], refs=[beam]) add_sec_to_fem(fem, fem_sec, fem_set) if beam.hinge_prop is None: return end1_p = beam.hinge_prop.end1.concept_node.p if beam.hinge_prop.end1 is not None else None end2_p = beam.hinge_prop.end2.concept_node.p if beam.hinge_prop.end2 is not None else None if beam.hinge_prop is not None: for el in elements: n1 = el.nodes[0] n2 = el.nodes[-1] el.hinge_prop = beam.hinge_prop if beam.hinge_prop.end1 is not None and vector_length(end1_p - n1.p) == 0.0: el.hinge_prop.end1.fem_node = n1 if beam.hinge_prop.end2 is not None and vector_length(end2_p - n2.p) == 0.0: el.hinge_prop.end2.fem_node = n2
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 local_z(self) -> np.ndarray: """Local Z describes the up vector of the cross section""" if self._local_z is not None: return self._local_z if self.type == ElemType.LINE: n1, n2 = self.elset.members[0].nodes[0], self.elset.members[0].nodes[-1] v = n2.p - n1.p if vector_length(v) == 0.0: logging.error(f"Element {self.elset.members[0].id} has zero length") xvec = [1, 0, 0] else: xvec = unit_vector(v) self._local_z = calc_zvec(xvec, self.local_y) elif self.type == ElemType.SHELL: self._local_z = normal_to_points_in_plane([n.p for n in self.elset.members[0].nodes]) else: from ada.core.constants import Z self._local_z = np.array(Z, dtype=float) return self._local_z
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 local_y(self) -> np.ndarray: """Local y describes the cross vector of the beams X and Z axis""" if self._local_y is not None: return self._local_y if self.type == ElemType.LINE: el = self.elset.members[0] n1, n2 = el.nodes[0], el.nodes[-1] v = n2.p - n1.p if vector_length(v) == 0.0: raise ValueError(f'Element "{el}" has no length. UNable to calculate y-vector') xvec = unit_vector(v) # See https://en.wikipedia.org/wiki/Cross_product#Coordinate_notation for order of cross product vec = calc_yvec(xvec, self.local_z) elif self.type == ElemType.SHELL: vec = calc_yvec(self.local_x, self.local_z) else: vec = calc_yvec(self.local_x, self.local_z) self._local_y = np.where(abs(vec) == 0, 0, vec) return self._local_y
def build_constraint(n_old, elem, ecc, i): n_new = edited_nodes[n_old.id] mat = np.eye(3) new_p = np.dot(mat, ecc) + n_old.p n_new_ = Node(new_p, parent=elem.parent) if vector_length(n_new_.p - n_new.p) > tol: elem.parent.nodes.add(n_new_, allow_coincident=True) m_set = FemSet(f"el{elem.id}_mpc{i + 1}_m", [n_new_], "nset") s_set = FemSet(f"el{elem.id}_mpc{i + 1}_s", [n_old], "nset") c = Constraint( f"el{elem.id}_mpc{i + 1}_co", Constraint.TYPES.MPC, m_set, s_set, mpc_type="Beam", parent=elem.parent, ) elem.parent.add_constraint(c) elem.nodes[i] = n_new_ edited_nodes[n_old.id] = n_new_ else: elem.nodes[i] = n_new edited_nodes[n_old.id] = n_new
def sort_nodes_by_distance(point: Union[Node, np.ndarray], nodes: list[Node]) -> list[Node]: if isinstance(point, Node): point = point.p return sorted(nodes, key=lambda x: vector_length(x.p - point))
def make_cylinder_from_points(p1, p2, r, t=None): vec = unit_vector(np.array(p2) - np.array(p1)) l = vector_length(np.array(p2) - np.array(p1)) return make_cylinder(p1, vec, l, r, t)
def _build_pipe(self): from ada.core.curve_utils import make_arc_segment segs = [] for p1, p2 in zip(self.points[:-1], self.points[1:]): if vector_length(p2.p - p1.p) == 0.0: logging.info("skipping zero length segment") continue segs.append([p1, p2]) segments = segs seg_names = Counter(prefix=self.name + "_") # 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 < len_tol or vlen2 == len_tol: logging.error( f'Segment Length is below point tolerance for unit "{self.units}". Skipping' ) continue 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: logging.error("No shared point found") if i != 0 and len(self._segments) > 0: pseg = self._segments[-1] prev_p = (pseg.p1.p, pseg.p2.p) else: prev_p = (p11.p, p12.p) try: seg1, arc, seg2 = make_arc_segment( prev_p[0], prev_p[1], p22.p, self.pipe_bend_radius * 0.99) except ValueError as e: logging.error( f"Error: {e}" ) # , traceback: "{traceback.format_exc()}"') continue except RuntimeError as e: logging.error( f"Error: {e}" ) # , traceback: "{traceback.format_exc()}"') continue if i == 0 or len(self._segments) == 0: 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: continue pseg = self._segments[-1] pseg.p2 = Node(seg1.p2, units=self.units) self._segments.append( PipeSegElbow( next(seg_names) + "_Elbow", 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, units=self.units), Node(seg2.p2, units=self.units), **props))
def visualize_it(res_file, temp_dir=".temp", default_index=0): import pathlib import meshio from ipygany import ColorBar, IsoColor, PolyMesh, Scene, Warp, colormaps from IPython.display import clear_output, display from ipywidgets import AppLayout, Dropdown, FloatSlider, VBox, jslink from ada.core.vector_utils import vector_length res_file = pathlib.Path(res_file).resolve().absolute() suffix = res_file.suffix.lower() suffix_map = {".rmed": "med", ".vtu": None} imesh = meshio.read(res_file, file_format=suffix_map[suffix]) imesh.point_data = { key.replace(" ", "_"): value for key, value in imesh.point_data.items() } def filter_keys(var): if suffix == ".vtu" and var != "U": return False if suffix == ".rmed" and var == "point_tags": return False return True warp_data = [key for key in filter(filter_keys, imesh.point_data.keys())] magn_data = [] for d in warp_data: res = [vector_length(v[:3]) for v in imesh.point_data[d]] res_norm = [r / max(res) for r in res] magn_data_name = f"{d}_magn" imesh.point_data[magn_data_name] = np.array(res_norm, dtype=np.float64) magn_data.append(magn_data_name) imesh.field_data = { key: np.array(value) for key, value in imesh.field_data.items() } tf = (pathlib.Path(temp_dir).resolve().absolute() / res_file.name).with_suffix(".vtu") if tf.exists(): os.remove(tf) os.makedirs(tf.parent, exist_ok=True) imesh.write(tf) mesh = PolyMesh.from_vtk(str(tf)) mesh.default_color = "gray" warp_vec = warp_data[default_index] try: colored_mesh = IsoColor(mesh, input=magn_data[default_index], min=0.0, max=1.0) except KeyError as e: trace_str = traceback.format_exc() logging.error(f'KeyError "{e}"\nTrace: "{trace_str}"') colored_mesh = mesh except ImportError as e: trace_str = traceback.format_exc() logging.error("This might be") logging.error(f'ImportError "{e}"\nTrace: "{trace_str}"') return warped_mesh = Warp(colored_mesh, input=warp_vec, warp_factor=0.0) warp_slider = FloatSlider(value=0.0, min=-1.0, max=1.0) jslink((warped_mesh, "factor"), (warp_slider, "value")) # Create a colorbar widget colorbar = ColorBar(colored_mesh) # Colormap choice widget colormap = Dropdown(options=colormaps, description="colormap:") jslink((colored_mesh, "colormap"), (colormap, "index")) # EigenValue choice widget eig_map = Dropdown(options=warp_data, description="Data Value:") scene = Scene([warped_mesh]) app = AppLayout(left_sidebar=scene, right_sidebar=VBox( (eig_map, warp_slider, colormap, colorbar)), pane_widths=[2, 0, 1]) def change_input(change): vec_name = change["new"] logging.info(vec_name) colored_mesh.input = vec_name + "_magn" warped_mesh.input = vec_name # Highly inefficient but likely needed due to bug https://github.com/QuantStack/ipygany/issues/69 clear_output() display(app) eig_map.observe(change_input, names=["value"]) return app