def generate_disk_mesh(radius=1.0, theta_max=np.pi, nr=2, ntheta=4, center=(0, 0, 0), normal=(1, 0, 0), name=None) -> Mesh: theta_range = np.linspace(0, 2 * theta_max, ntheta + 1) r_range = np.linspace(0.0, radius, nr + 1) nodes = np.zeros(((ntheta + 1) * (nr + 1), 3), dtype=float) for i, (r, t) in enumerate(product(r_range, theta_range)): y = +r * np.sin(t) z = -r * np.cos(t) nodes[i, :] = (0, y, z) panels = np.zeros((ntheta * nr, 4), dtype=int) for k, (i, j) in enumerate(product(range(0, nr), range(0, ntheta))): panels[k, :] = (j + i * (ntheta + 1), j + 1 + i * (ntheta + 1), j + 1 + (i + 1) * (ntheta + 1), j + (i + 1) * (ntheta + 1)) mesh = Mesh(nodes, panels, name=name) mesh.merge_duplicates() mesh.heal_triangles() mesh.rotate_around_center_to_align_vectors( (0, 0, 0), mesh.faces_normals[0], normal) mesh.translate(center) return mesh
def merged(self, name=None) -> Mesh: """Merge the sub-meshes and return a full mesh. If the collection contains other collections, they are merged recursively. Optionally, a new name can be given to the resulting mesh.""" if name is None: name = self.name merged = Mesh(self.vertices, self.faces, name=name) merged.merge_duplicates() merged.heal_triangles() return merged
def from_profile(profile: Union[Callable, Iterable[float]], z_range: Iterable[float]=np.linspace(-5, 0, 20), axis: Axis=Oz_axis, nphi: int=20, name=None): """Return a floating body using the axial symmetry. The shape of the body can be defined either with a function defining the profile as [f(z), 0, z] for z in z_range. Alternatively, the profile can be defined as a list of points. The number of vertices along the vertical direction is len(z_range) in the first case and profile.shape[0] in the second case. Parameters ---------- profile : function(float → float) or array(N, 3) define the shape of the body either as a function or a list of points. z_range: array(N), optional used only if the profile is defined as a function. axis : Axis symmetry axis nphi : int, optional number of vertical slices forming the body name : str, optional name of the generated body (optional) Returns ------- AxialSymmetricMesh the generated mesh """ if name is None: name = "axisymmetric_mesh" if callable(profile): z_range = np.asarray(z_range) x_values = [profile(z) for z in z_range] profile_array = np.stack([x_values, np.zeros(len(z_range)), z_range]).T else: profile_array = np.asarray(profile) assert len(profile_array.shape) == 2 assert profile_array.shape[1] == 3 n = profile_array.shape[0] angle = 2 * np.pi / nphi nodes_slice = np.concatenate([profile_array, axis.rotate_points(profile_array, angle)]) faces_slice = np.array([[i, i+n, i+n+1, i+1] for i in range(n-1)]) body_slice = Mesh(nodes_slice, faces_slice, name=f"slice_of_{name}") body_slice.merge_duplicates() body_slice.heal_triangles() return AxialSymmetricMesh(body_slice, axis=axis, nb_repetitions=nphi - 1, name=name)
def _generate_sphere_mesh(self, ntheta, nphi, clip_free_surface=False, name=None): if clip_free_surface: if self.geometric_center[2] < -self.radius: # fully immersed theta_max = np.pi elif self.geometric_center[2] < self.radius: theta_max = np.arccos(self.geometric_center[2] / self.radius) else: raise Exception("Sphere out of the water") else: theta_max = np.pi theta = np.linspace(0.0, theta_max, ntheta + 1) phi = np.linspace(-np.pi, np.pi, nphi + 1) # Nodes nodes = np.zeros(((ntheta + 1) * (nphi + 1), 3), dtype=float) for i, (t, p) in enumerate(product(theta, phi)): # The sign of theta below is a trick to get the correct orientation of the normal vectors... x = +np.sin(t) * np.sin(np.sign(t) * p) y = +np.sin(t) * np.cos(np.sign(t) * p) z = -np.cos(t) nodes[i, :] = (x, y, z) nodes *= self.radius # Connectivity panels = np.zeros((ntheta * nphi, 4), dtype=int) for k, (i, j) in enumerate(product(range(0, ntheta), range(0, nphi))): panels[k, :] = (j + i * (nphi + 1), j + (i + 1) * (nphi + 1), j + 1 + (i + 1) * (nphi + 1), j + 1 + i * (nphi + 1)) mesh = Mesh(nodes, panels, name=f"{name}_mesh") mesh.merge_duplicates() mesh.heal_triangles() return mesh