def __init__( self, cell_nums, # 2D or 3D int array. E, # Young's modulus. nu, # Poisson's ratio. vol_tol, # vol_tol <= mixed cell <= 1 - vol_tol. edge_sample_num, # The number of samples inside each cell's axis. folder # The folder that stores temporary files. ): # The following data members are provided: # - _cell_nums and _node_nums; # - _dim; # - _cell_options; # - _folder; # - _parametric_shape_info: a list of (string, int); # - _node_boundary_info: a list of (int, float) or ((int, int, int), float) (2D) or # ((int, int, int, int), float) (3D) that describes the boundary conditions. # - _interface_boundary_type: either 'no-slip' or 'free-slip' (default). # Sanity check the inputs. cell_nums = ndarray(cell_nums).astype(np.int32) assert cell_nums.size in [2, 3] # The value of E does not quite matter as it simply scale the QP problem by a constant factor. E = float(E) assert E > 0 nu = float(nu) assert 0 < nu < 0.5 vol_tol = float(vol_tol) assert 0 < vol_tol < 0.5 edge_sample_num = int(edge_sample_num) assert edge_sample_num > 1 if folder is not None: folder = Path(folder) create_folder(folder, exist_ok=True) # Create data members. self._cell_nums = np.copy(cell_nums) self._node_nums = ndarray([n + 1 for n in cell_nums]).astype(np.int32) self._dim = self._cell_nums.size self._cell_options = { 'E': E, 'nu': nu, 'vol_tol': vol_tol, 'edge_sample_num': edge_sample_num } self._folder = folder ########################################################################### # Derived classes should implement these data members. ########################################################################### self._parametric_shape_info = [] self._node_boundary_info = [] self._interface_boundary_type = 'free-slip'
def _render_3d(self, xk, img_name, options): assert self._folder _, info = self.solve(xk, False, options) # For the basic _render_2d function, we assume mode = 1. mode_num = len(info) for m in range(mode_num): mode_folder = Path(self._folder / 'mode_{:04d}'.format(m)) create_folder(mode_folder, exist_ok=True) scene = info[m]['scene'] u_field = info[m]['velocity_field'] # A proper range for placing the scene is [-0.4, 0.4] x [-0.3, 0.3] x [0, 0.6]. render_options = { 'file_name': str(mode_folder / img_name), 'resolution': (1024, 768), 'light_map': 'uffizi-large.exr', 'light_map_scale': 0.75, 'sample': options['spp'], 'max_depth': 2, 'camera_pos': (0.1, -0.65, 1.1), 'camera_lookat': (0, 0, 0), } renderer = PbrtRenderer(render_options) renderer.add_tri_mesh(Path(root_path) / 'asset/mesh/plane.obj', transforms=[('s', 1.5)], color=(.4, .4, .4), texture_img='background.png') # Render the solid-fluid interface. cx, cy, cz = self._cell_nums nx, ny, nz = self._node_nums # How to use cmap: # cmap(0.0) to cmap(1.0) covers the whole range of the colormap. cmap = plt.get_cmap('jet') scale = np.min([0.8 / cx, 0.6 / cy, 0.6 / cz]) transforms = [('t', (-cx / 2, -cy / 2, 0)), ('s', scale)] # Assemble an obj mesh. image_prefix = '.'.join(img_name.split('.')[:-1]) interface_file_name = mode_folder / '{}.obj'.format(image_prefix) sdf = np.zeros((nx, ny, nz)) for i in range(nx): for j in range(ny): for k in range(nz): sdf[i, j, k] = scene.GetSignedDistance((i, j, k)) sdf = ndarray(sdf) verts, faces, _, _ = measure.marching_cubes_lewiner(sdf, 0) verts = ndarray(verts) faces = ndarray(faces).astype(np.int32) + 1 # Write obj files. with open(interface_file_name, 'w') as f: for v in verts: f.write('v {:6f} {:6f} {:6f}\n'.format(*v)) for fi in faces: f.write('f {:d} {:d} {:d}\n'.format(*fi)) renderer.add_tri_mesh(interface_file_name, transforms=transforms, color=(1.0, 0.61, 0.0)) # Render the velocity field. lines = [] max_u_len = -np.inf for i in range(nx): for j in range(ny): for k in range(nz): if scene.GetSignedDistance((i, j, k)) >= 0: continue v_begin = ndarray([i, j, k]) v_end = v_begin + u_field[i, j, k] u_len = np.linalg.norm(u_field[i, j, k]) if u_len > max_u_len: max_u_len = u_len lines.append((v_begin, v_end)) # Scale the line lengths so that the maximum length is 1/10 of the longest axis. lines_scale = 0.1 * np.max(self._cell_nums) / max_u_len # width is 1/10 of the cell size. width = 0.1 for v_begin, v_end in lines: # Compute the color. color_idx = self._color_velocity(v_end - v_begin) color = ndarray(cmap(color_idx))[:3] v0 = v_begin v3 = (v_end - v_begin) * lines_scale + v_begin v1 = (2 * v0 + v3) / 3 v2 = (v0 + 2 * v3) / 3 renderer.add_shape_mesh( { 'name': 'curve', 'point': ndarray([v0, v1, v2, v3]), 'width': width }, color=color, transforms=transforms) renderer.render(verbose=True)
def _render_2d(self, xk, img_name, options): assert self._folder loss, info = self.solve(xk, False, options) # For the basic _render_2d function, we assume mode = 1. mode_num = len(info) for m in range(mode_num): mode_folder = Path(self._folder / 'mode_{:04d}'.format(m)) create_folder(mode_folder, exist_ok=True) scene = info[m]['scene'] u_field = info[m]['velocity_field'] cx, cy = self._cell_nums face_color = ndarray([247 / 255, 247 / 255, 247 / 255]) plt.rcParams['figure.facecolor'] = face_color plt.rcParams['axes.facecolor'] = face_color fig = plt.figure(figsize=(12, 10)) ax = fig.add_subplot(111) padding = 5 ax.set_title('Loss: {:3.6e}'.format(loss)) ax.set_xticks([]) ax.set_yticks([]) ax.set_xlim([-padding, cx + padding]) ax.set_ylim([-padding, cy + padding]) ax.set_aspect('equal') ax.axis('off') # Plot cells. lines = [] colors = [] shift = 0.0 fluidic_node = np.ones((cx + 1, cy + 1)) for i in range(cx): for j in range(cy): if scene.IsFluidCell((i, j)): color = 'k' elif scene.IsSolidCell((i, j)): color = 'k' fluidic_node[i, j] = fluidic_node[ i + 1, j] = fluidic_node[i, j + 1] = fluidic_node[i + 1, j + 1] = 0 else: color = 'k' pts = [(i + shift, j + shift), (i + 1 - shift, j + shift), (i + 1 - shift, j + 1 - shift), (i + shift, j + 1 - shift)] lines += [(pts[0], pts[1]), (pts[1], pts[2]), (pts[2], pts[3]), (pts[3], pts[0])] colors += [ color, ] * 4 ax.add_collection( mc.LineCollection(lines, colors=colors, linewidth=0.5)) # Plot velocity fields. cmap = plt.get_cmap('coolwarm') lines = [] colors = [] u_min = np.inf u_max = -np.inf for i in range(cx + 1): for j in range(cy + 1): uij = u_field[i, j] uij_norm = np.linalg.norm(uij) if uij_norm > 0: if uij_norm > u_max: u_max = uij_norm if uij_norm < u_min: u_min = uij_norm for i in range(cx + 1): for j in range(cy + 1): if not fluidic_node[i, j]: continue uij = u_field[i, j] uij_norm = np.linalg.norm(uij) v0 = ndarray([i, j]) v1 = v0 + uij lines.append((v0, v1)) # Determine the color. color = cmap((uij_norm - u_min) / (u_max - u_min)) colors.append(color) ax.add_collection( mc.LineCollection(lines, colors=colors, linewidth=1.0)) # Plot solid-fluid interfaces. lines = [] def cutoff(d0, d1): assert d0 * d1 <= 0 # (0, d0), (t, 0), (1, d1). # t / -d0 = 1 / (d1 - d0) return -d0 / (d1 - d0) for i in range(cx): for j in range(cy): if not scene.IsMixedCell((i, j)): continue ps = [(i, j), (i + 1, j), (i + 1, j + 1), (i, j + 1)] ds = [scene.GetSignedDistance(p) for p in ps] ps = ndarray(ps) vs = [] for k in range(4): k_next = (k + 1) % 4 if ds[k] * ds[k_next] <= 0: t = cutoff(ds[k], ds[k_next]) vs.append((1 - t) * ps[k] + t * ps[k_next]) vs_len = len(vs) for k in range(vs_len): lines.append((vs[k], vs[(k + 1) % vs_len])) ax.add_collection( mc.LineCollection(lines, colors='tab:orange', linewidth=1)) # Plot other customized data if needed. self._render_customized_2d(scene, ax) fig.savefig(mode_folder / img_name) plt.close()
if __name__ == '__main__': # This script assumes the data folder exists. data_folder = Path('amplifier') cnt = 0 while True: data_file_name = data_folder / '{:04d}.data'.format(cnt) if not os.path.exists(data_file_name): cnt -= 1 break cnt += 1 data_file_name = data_folder / '{:04d}.data'.format(cnt) opt_history = pickle.load(open(data_file_name, 'rb')) # Setting up the environment. folder = Path('draw_design') create_folder(folder, exist_ok=True) seed = 42 env = AmplifierEnv2d(seed, folder) create_folder(folder / 'init_design', exist_ok=True) def draw_init_design(design_params, file_name, draw_control_points=False): _, info = env.solve(design_params, False, {'solver': 'eigen'}) u = info[0]['velocity_field'] node_nums = env.node_nums() sdf = np.zeros(node_nums) for i in range(node_nums[0]): for j in range(node_nums[1]): sdf[i, j] = info[0]['scene'].GetSignedDistance((i, j)) if sdf[i, j] >= 0: u[i, j] = 0
def __init__(self, options=None): self.__temporary_folder = Path('.tmp') create_folder(self.__temporary_folder) if options is None: options = {} # Image metadata. file_name = options['file_name'] if 'file_name' in options else 'output.exr' file_name = str(file_name) assert file_name.endswith('.png') or file_name.endswith('.exr') file_name_only = file_name[:-4] self.__file_name_only = file_name_only resolution = options['resolution'] if 'resolution' in options else (800, 800) resolution = tuple(resolution) assert len(resolution) == 2 resolution = [int(r) for r in resolution] self.__resolution = tuple(resolution) sample = options['sample'] if 'sample' in options else 4 sample = int(sample) assert sample > 0 self.__sample = sample max_depth = options['max_depth'] if 'max_depth' in options else 4 max_depth = int(max_depth) assert max_depth > 0 self.__max_depth = max_depth # Camera metadata. camera_pos = options['camera_pos'] if 'camera_pos' in options else (2, -2.2, 2) camera_pos = ndarray(camera_pos).ravel() assert camera_pos.size == 3 self.__camera_pos = camera_pos camera_lookat = options['camera_lookat'] if 'camera_lookat' in options else (0.5, 0.5, 0.5) camera_lookat = ndarray(camera_lookat).ravel() assert camera_lookat.size == 3 self.__camera_lookat = camera_lookat camera_up = options['camera_up'] if 'camera_up' in options else (0, 0, 1) camera_up = ndarray(camera_up).ravel() assert camera_up.size == 3 self.__camera_up = camera_up fov = options['fov'] if 'fov' in options else 33 fov = float(fov) assert 0 < fov < 90 self.__fov = fov # Lighting. lightmap = options['light_map'] if 'light_map' in options else 'lightmap.exr' lightmap = Path(root_path) / 'asset/texture/{}'.format(lightmap) self.__lightmap = lightmap lightmap_scale = options['light_map_scale'] if 'light_map_scale' in options else 1.0 lightmap_scale = float(lightmap_scale) self.__lightmap_scale = lightmap_scale # A list of objects. self.__tri_objects = [] self.__shape_objects = []
import sys sys.path.append('../') import numpy as np from pathlib import Path import matplotlib.pyplot as plt import matplotlib.colors from matplotlib import collections as mc from py_diff_stokes_flow.env.refinement_env_2d import RefinementEnv2d from py_diff_stokes_flow.common.common import ndarray, print_error, create_folder if __name__ == '__main__': folder = Path('draw_pipeline') create_folder(folder, exist_ok=True) nu = 0.45 scale = 0.75 env = RefinementEnv2d(0.45, scale) _, info = env.solve(env.sample(), False, {'solver': 'eigen'}) u = info[0]['velocity_field'] node_nums = env.node_nums() sdf = np.zeros(node_nums) for i in range(node_nums[0]): for j in range(node_nums[1]): sdf[i, j] = info[0]['scene'].GetSignedDistance((i, j)) if sdf[i, j] >= 0: u[i, j] = 0 # Draw design parameters.
import sys sys.path.append('../') from pathlib import Path import shutil import os import numpy as np from py_diff_stokes_flow.common.renderer import PbrtRenderer from py_diff_stokes_flow.common.common import print_info, create_folder from py_diff_stokes_flow.common.project_path import root_path if __name__ == '__main__': folder = Path('pbrt_renderer_demo') create_folder(folder) # Render. options = { 'file_name': str(folder / 'demo.png'), 'light_map': 'uffizi-large.exr', 'sample': 16, 'max_depth': 4, 'camera_pos': (0, -2, 0.8), 'camera_lookat': (0, 0, 0), } renderer = PbrtRenderer(options) renderer.add_tri_mesh(Path(root_path) / 'asset/mesh/curved_ground.obj', texture_img='chkbd_24_0.7') renderer.add_tri_mesh(Path(root_path) / 'asset/mesh/bunny.obj', transforms=[ ('s', 0.4),