def optimize(inputfile, mode=None): # ---------- SETUP ---------- # handle input of data with open(inputfile) as datafile: data = json.load(datafile) geometry = data["geometry"] U, V, cpoints, weights, plotrange = gm.nurbs(geometry["shape"], geometry["params"]) tdrefine, sdrefine = geometry["refine"] optparams = data["optimization"] tarefine, sarefine = optparams["refine"] # assume open knot vectors p = np.count_nonzero(U == U[0]) - 1 q = np.count_nonzero(V == V[0]) - 1 # apply refinement projcpoints = np.dstack((cpoints * np.atleast_3d(weights), weights)) projcpoints, Ud, Vd = al.refine(projcpoints, p, q, U, V, tdrefine, sdrefine) designweights = projcpoints[:, :, -1] projcpoints, U, V = al.refine(projcpoints, p, q, Ud, Vd, tarefine, sarefine) cpoints = projcpoints[..., :-1] / projcpoints[..., -1:] weights = projcpoints[:, :, -1] # geometric properties n = U.size - p - 1 m = V.size - q - 1 nd = Ud.size - p - 1 md = Vd.size - q - 1 nel0 = len(set(U)) - 1 nel1 = len(set(V)) - 1 # build localization tools C0 = al.bezier_extract(U, p, n) C1 = al.bezier_extract(V, q, m) Cd0 = al.bezier_extract(Ud, p, nd) Cd1 = al.bezier_extract(Vd, q, md) INN, IEN = al.build_inc_ien(U, V, p, q, n, m) INNd, IENd = al.build_inc_ien(Ud, Vd, p, q, nd, md) # handle loading situation load = data["load"] free, t = gm.load(cpoints, nel0, nel1, load["type"], load["params"]) ndofs, ID = al.build_id(INN, free) # convenience arrays IEN0 = INN[IEN][..., 0] IEN1 = INN[IEN][..., 1] IENd0 = INNd[IENd][..., 0] IENd1 = INNd[IENd][..., 1] IDE = ID[IEN] # determine integration points and weights (map from [-1, 1] to [0, 1]) intpoints0, intweights0 = np.polynomial.legendre.leggauss(p + 1) intpoints1, intweights1 = np.polynomial.legendre.leggauss(q + 1) intpoints0 = (intpoints0 + 1.) / 2. intpoints1 = (intpoints1 + 1.) / 2. intweights0 /= 2 intweights1 /= 2 intweights = np.reshape(np.einsum("j, i", intweights0, intweights1), -1) # obtain shape arrays P = cpoints[IEN1, IEN0, :] W = weights[IEN1, IEN0, np.newaxis] R, Rb, dRdX, J, Jb = ro.shape(p, q, C0, C1, nel0, P, W, intpoints0, intpoints1) # define a way to calculate the normalized volume in use domainvolume = np.einsum("i, ji ->", intweights, J) def volume(rho): return np.einsum("i, jk, jki, ji", intweights, rho, R, J) / domainvolume # set up force vector for analysis '''wJ0 = np.einsum("j, ij -> ij", intweights0, J0) wJ1 = np.einsum("j, ij -> ij", intweights1, J1) wR0 = np.einsum("ijk, ik -> ij", R0, wJ0) wR1 = np.einsum("ijk, ik -> ij", R1, wJ1)''' f = ro.force(n, m, IDE, IEN0, IEN1, intweights0, intweights1, ndofs, Rb, Jb, t, load["force"]) # calculate the "B matrices" B = ro.computeB(R, dRdX) # compute the "ground D matrix", assuming plain stress material = data["material"] E = material["E"] nu = material["nu"] Dhat = E / (1 - nu * nu) * np.array([[1, nu, 0], [nu, 1, 0], [0, 0, (1 - nu) / 2.]]) # calculate the "ground" stiffness matrices Khat = np.einsum("ijmk, mn, ijnl-> ijkl", B, Dhat, B) # determine design/analysis conversion array and design basis darr = ro.designarray(nel0, nel1, tarefine, sarefine) Wd = designweights[IENd1, IENd0, np.newaxis] Rd = ro.designbasis(p, q, nel0, nel1, Cd0, Cd1, Wd, intpoints0, intpoints1, tarefine, sarefine, darr) # set up a starting density if optparams["startdens"]: rhod = pickle.load(open(optparams["startdens"], "rb")) else: rhod = np.full(P.shape[:-1], optparams["volfrac"]) # compute A matrix for gradient determination A = ro.projectionmatrix(nd, md, IENd, intweights, Rd, J, darr) # prepare x and y arrays for plotting plot = data["plot"] plotpoints = np.linspace(0, 1 - 1E-6, plot["npoints"]) Rdummy, X, Y = ro.plotprepare(p, q, nel0, nel1, C0, C1, P, W, plotpoints) # retrieve filenames plotfile, densfile, histfile = ro.filenames(optparams["filepath"], optparams["filename"], plot["extension"]) # ---------- OPTIONAL DOMAIN PLOT ---------- if "domain" in optparams: ai.plotdomain(p, q, nel0, nel1, C0, C1, P, W, plotpoints, plot["width"], plotrange, plotfile) sys.exit() # ---------- OPTIMIZATION ---------- iteration = 0 compliances = [] fullit = (optparams["pmax"] - 1) / optparams["pperit"] starttime = datetime.datetime.now() rhovals = np.einsum("il, ilj -> ij", rhod, Rd) gd = np.empty_like(rhod) alpha = None while True: # compute and assemble stiffness matrix simpfac = min(1 + optparams["pperit"] * iteration, optparams["pmax"]) simpmargin = 1E-6 prho = (1 - simpmargin) * rhovals**simpfac + simpmargin stiffdata = np.einsum("l, il, iljk, il -> ijk", intweights, prho, Khat, J) K = ro.stiffness(IEN, IDE, ndofs, stiffdata) # solve for displacement (ensuring symmetry for efficiency) K = (K + K.transpose()) / 2. umat = sl.spsolve(K, f) u = umat[IDE] u[IDE == -1] = 0 # determine compliance and matrix/vector for gradient computation #eps = np.einsum("ijkl, il -> ijk", B, np.reshape(u, (IEN.shape[0], -1))) prhodiff = (1 - simpmargin) * simpfac * rhovals**(simpfac - 1) compliance, b = ro.projectionvector(nd, md, IENd, intweights, Rd, J, Khat, prho, prhodiff, u, darr) compliances.append(compliance) # alternatively, K could be used to determine compliance # solve for gradient A = (A + A.transpose()) / 2. gdmat = sl.spsolve(A, b) gd[darr, ...] = gdmat[IENd][:, np.newaxis, :] # update density if not alpha: # step size change_ratio = 1 #alpha = np.abs(rho.mean() / g.mean() * change_ratio) alpha = np.abs( np.linalg.norm(rhod) / np.linalg.norm(gd) * change_ratio) rhod, rhodold = ro.update(volume, optparams["volfrac"], rhod, gd, alpha) rhovals = np.einsum("il, ilj -> ij", rhod, Rd) # temp print datetime.datetime.now().strftime( "%H:%M:%S"), iteration, compliance # provide stop conditions if iteration >= optparams["maxit"] - 1: break if iteration >= fullit + 30: if np.max(np.absolute(rhod - rhodold)) <= 1E-3: break #if np.average(compliances[-10:]) >= np.average(compliances[-30:]): # break iteration += 1 # temp totaltime = datetime.datetime.now() - starttime print "done in {} seconds".format(totaltime.total_seconds()) # back up density, write compliance history pickle.dump(rhod, open(densfile, "wb")) pickle.dump( { "compliances": compliances, "n_it": iteration, "time": totaltime }, open(histfile, "wb")) # calculate basis functions for plotting Rp = ro.designbasis(p, q, nel0, nel1, Cd0, Cd1, Wd, plotpoints, plotpoints, tarefine, sarefine, darr) # ---------- OPTIONAL GRADIENT PLOT ---------- # use only after first iteration if "airy" in optparams: graddata = np.reshape(np.einsum("il, ilj -> ij", gd, Rp), (-1, plot["npoints"], plot["npoints"])) effE = E * (optparams["volfrac"]**2) airydata = ai.airystress(X, Y - 0.5, load["force"][3], 0.5, 1, effE, nu) ai.gradplot(X, Y, nel0, nel1, graddata, airydata, plotpoints, plot["width"], plotrange, plotfile) sys.exit() # plot density densdata = np.reshape(np.einsum("il, ilj -> ij", rhod, Rp), (-1, plot["npoints"], plot["npoints"])) ro.plot(X, Y, nel0, nel1, densdata, plotpoints, plot["width"], plotrange, plotfile)
def optimize(inputfile): # ---------- SETUP ---------- # handle input of data with open(inputfile) as datafile: data = json.load(datafile) geometry = data["geometry"] U, V, cpoints, weights, plotrange = gm.nurbs(geometry["shape"], geometry["params"]) tdrefine, sdrefine = geometry["refine"] optparams = data["optimization"] tarefine, sarefine = optparams["refine"] # assume open knot vectors p = np.count_nonzero(U == U[0]) - 1 q = np.count_nonzero(V == V[0]) - 1 # apply refinement projcpoints = np.dstack((cpoints * np.atleast_3d(weights), weights)) projcpoints, U, V = al.refine(projcpoints, p, q, U, V, tdrefine, sdrefine) neld0 = len(set(U)) - 1 neld1 = len(set(V)) - 1 projcpoints, U, V = al.refine(projcpoints, p, q, U, V, tarefine, sarefine) cpoints = projcpoints[..., :-1] / projcpoints[..., -1:] weights = projcpoints[:, :, -1] # geometric properties n = U.size - p - 1 m = V.size - q - 1 nel0 = len(set(U)) - 1 nel1 = len(set(V)) - 1 # build localization tools C0 = al.bezier_extract(U, p, n) C1 = al.bezier_extract(V, q, m) INN, IEN = al.build_inc_ien(U, V, p, q, n, m) # handle loading situation load = data["load"] free, t = gm.load(cpoints, nel0, nel1, load["type"], load["params"]) ndofs, ID = al.build_id(INN, free) # convenience arrays IEN0 = INN[IEN][..., 0] IEN1 = INN[IEN][..., 1] IDE = ID[IEN] # determine integration points and weights (map from [-1, 1] to [0, 1]) intpoints0, intweights0 = np.polynomial.legendre.leggauss(p + 1) intpoints1, intweights1 = np.polynomial.legendre.leggauss(q + 1) intpoints0 = (intpoints0 + 1.) / 2. intpoints1 = (intpoints1 + 1.) / 2. intweights0 /= 2 intweights1 /= 2 intweights = np.reshape(np.einsum("j, i", intweights0, intweights1), -1) # obtain shape arrays P = cpoints[IEN1, IEN0, :] W = weights[IEN1, IEN0, np.newaxis] R, Rb, dRdX, J, Jb = ro.shape(p, q, C0, C1, nel0, P, W, intpoints0, intpoints1) # it would be more efficient to work with a Rd for plotting as well # set up force vector for analysis f = ro.force(n, m, IDE, IEN0, IEN1, intweights0, intweights1, ndofs, Rb, Jb, t, load["force"]) # calculate the "B matrices" B = ro.computeB(R, dRdX) # compute the "ground D matrix", assuming plain stress material = data["material"] E = material["E"] nu = material["nu"] Dhat = E / (1 - nu * nu) * np.array([[1, nu, 0], [nu, 1, 0], [0, 0, (1 - nu) / 2.]]) # calculate the "ground" stiffness matrices Khat = np.einsum("l, ilmj, mn, ilnk, il -> ijk", intweights, B, Dhat, B, J) # set up design/analysis conversion array and starting density darr = ro.designarray(nel0, nel1, tarefine, sarefine) if optparams["startdens"]: rhod = pickle.load(open(optparams["startdens"], "rb")) else: rhod = np.full(neld0 * neld1, optparams["volfrac"]) # define a way to calculate the normalized volume in use elementvolume = np.einsum("j, ij -> i", intweights, J) elementvolume = np.sum(elementvolume[darr], axis=1) elementvolume /= np.sum(elementvolume) def volume(rhod): return np.einsum("i, i", rhod, elementvolume) # prepare x and y arrays for plotting plot = data["plot"] plotpoints = np.linspace(0, 1 - 1E-6, plot["npoints"]) Rp, X, Y = ro.plotprepare(p, q, nel0, nel1, C0, C1, P, W, plotpoints) # define filter method if optparams["rmin"]: H, Hs = al.density_filter(neld0, neld1, optparams["rmin"]) def densfilter(x): return H.dot(x / Hs) else: def densfilter(x): return x # retrieve filenames plotfile, densfile, histfile = ro.filenames(optparams["filepath"], optparams["filename"], plot["extension"]) # ---------- OPTIONAL DOMAIN PLOT ---------- if "domain" in optparams: ai.plotdomain(p, q, nel0, nel1, C0, C1, P, W, plotpoints, plot["width"], plotrange, plotfile) sys.exit() # ---------- OPTIMIZATION ---------- iteration = 0 compliances = [] fullit = (optparams["pmax"] - 1) / optparams["pperit"] starttime = datetime.datetime.now() rho = np.zeros(P.shape[0]) rho[darr] = rhod[:, np.newaxis] alpha = None while True: # compute and assemble stiffness matrix simpfac = min(1 + optparams["pperit"] * iteration, optparams["pmax"]) simpmargin = 1E-6 prho = (1 - simpmargin) * rho**simpfac + simpmargin stiffdata = np.einsum("i, ijk -> ijk", prho, Khat) K = ro.stiffness(IEN, IDE, ndofs, stiffdata) # solve for displacement (ensuring symmetry for efficiency) K = (K + K.transpose()) / 2. umat = sl.spsolve(K, f) u = umat[IDE] u[IDE == -1] = 0 # determine compliance and gradient #eps = np.einsum("ijkl, il -> ijk", B, np.reshape(u, (IEN.shape[0], -1))) prhodiff = (1 - simpmargin) * simpfac * rho**(simpfac - 1) u1d = np.reshape(u, (IEN.shape[0], -1)) Chat = np.einsum("ij, ijk, ik -> i", u1d, Khat, u1d) compliance = np.einsum("i, i", prho, Chat) compliances.append(compliance) # alternatively, K could be used to determine compliance g = -prhodiff * Chat # sum up gradients per "design element" and adjust original values accordingly gd = densfilter(np.sum(g[darr], axis=1)) """if iteration > 20: densdata = np.reshape(np.einsum("il, ilj -> ij", g, Rp), (-1, 10, 10)) densdata[np.abs(densdata) < 1E-3*np.abs(densdata).mean()] = 0 densdata = np.clip(densdata, 0, 3*densdata.mean()) ro.plot(X, Y, p, q, nel0, nel1, densdata)""" #g[np.abs(g) < 1E-3 * np.abs(g).mean()] = 0 # update density if not alpha: # step size change_ratio = 0.1 #alpha = np.abs(rho.mean() / g.mean() * change_ratio) alpha = np.abs( np.linalg.norm(rhod) / np.linalg.norm(gd) * change_ratio) rhod, rhodold = ro.update(volume, optparams["volfrac"], rhod, gd, alpha, densfilter) rho[darr] = rhod[:, np.newaxis] # temp print datetime.datetime.now().strftime( "%H:%M:%S"), iteration, compliance # provide stop conditions if iteration >= optparams["maxit"] - 1: break if iteration >= fullit + 30: if np.max(np.absolute(rhod - rhodold)) <= 1E-3: break #if np.average(compliances[-10:]) >= np.average(compliances[-30:]): # break iteration += 1 # temp totaltime = datetime.datetime.now() - starttime print "done in {} seconds".format(totaltime.total_seconds()) # back up element density, write compliance history pickle.dump(rhod, open(densfile, "wb")) pickle.dump( { "compliances": compliances, "n_it": iteration, "time": totaltime }, open(histfile, "wb")) # ---------- OPTIONAL GRADIENT PLOT ---------- # use only after first iteration if "airy" in optparams: g[darr] = gd graddata = np.tile(g[:, np.newaxis, np.newaxis], (1, plot["npoints"], plot["npoints"])) XA, XB, YA, YB = ai.elementlimits( nel0, nel1, 1. * geometry["params"]["aspectratio"] / nel0, 1. / nel1) effE = E * (optparams["volfrac"]**2) airydata = ai.airystressint(XA, XB, YA - 0.5, YB - 0.5, load["force"][3], 0.5, 1, effE, nu) airydata = np.tile(airydata[:, np.newaxis, np.newaxis], (1, plot["npoints"], plot["npoints"])) ai.elgradplot(X, Y, nel0, nel1, graddata, airydata, plotpoints, plot["width"], plotrange, plotfile) sys.exit() # plot density densdata = np.tile(rho[:, np.newaxis, np.newaxis], (1, plot["npoints"], plot["npoints"])) ro.plot(X, Y, nel0, nel1, densdata, plotpoints, plot["width"], plotrange, plotfile)
geometry.set_position((0.01 - box_width / 2, -box_height / 4, 0)) material = rtx.EmissiveMaterial(1.0) mapping = rtx.SolidColorMapping((1, 1, 1)) light = rtx.Object(geometry, material, mapping) scene.add(light) geometry = rtx.PlainGeometry(box_width / 2, box_width / 2) geometry.set_rotation((0, -math.pi / 2, 0)) geometry.set_position((box_width / 2 - 0.01, -box_height / 4, 0)) material = rtx.EmissiveMaterial(1.0) mapping = rtx.SolidColorMapping((0, 1, 1)) light = rtx.Object(geometry, material, mapping) scene.add(light) # place bunny faces, vertices = gm.load("../geometries/bunny") bottom = np.amin(vertices, axis=0) geometry = rtx.StandardGeometry(faces, vertices, 25) geometry.set_position((0, -box_height / 2 - (bottom[1] + 0.01) * 3, 0)) geometry.set_scale((3, 3, 3)) material = rtx.LambertMaterial(0.95) mapping = rtx.SolidColorMapping((1, 1, 1)) bunny = rtx.Object(geometry, material, mapping) scene.add(bunny) screen_width = 768 screen_height = 512 rt_args = rtx.RayTracingArguments() rt_args.num_rays_per_pixel = 128 rt_args.max_bounce = 4
import geometry as gm import matplotlib.pyplot as plt scene = rtx.Scene() # floor geometry = rtx.PlainGeometry(100, 100) geometry.set_position((0, 0, 0)) geometry.set_rotation((-math.pi / 2, 0, 0)) material = rtx.LambertMaterial(1.0) mapping = rtx.SolidColorMapping((1, 1, 1)) floor = rtx.Object(geometry, material, mapping) scene.add(floor) # place bunny faces, vertices = gm.load("../geometries/bunny") bottom = np.amin(vertices, axis=0) geometry = rtx.StandardGeometry(faces, vertices, 25) geometry.set_position((-2.25, -bottom[2], 0)) material = rtx.LambertMaterial(1.0) mapping = rtx.SolidColorMapping((0, 1, 0)) bunny = rtx.Object(geometry, material, mapping) scene.add(bunny) # place teapot faces, vertices = gm.load("../geometries/teapot") bottom = np.amin(vertices, axis=0) geometry = rtx.StandardGeometry(faces, vertices, 25) geometry.set_position((-0.75, -bottom[2] * 1.5, 0)) geometry.set_scale((1.5, 1.5, 1.5)) material = rtx.LambertMaterial(1.0)