def __init__( self, nx, # target number of points in x ny, # target number of points in y rho, # density profile, must be 1D function xmin, # (xmin, ymin) coordinates xmax, # (xmax, ymax) coordinates nNodePerh=2.01, numbins=10000, SPH=False): assert nx > 0 assert ny > 0 assert xmin[0] < xmax[0] assert xmin[1] < xmax[1] assert nNodePerh > 0.0 # First use the 1D generator to generate a 1D slice profile along x. gen1d = GenerateNodeProfile1d(nx=nx, rho=rho, xmin=xmin[0], xmax=xmax[0], nNodePerh=nNodePerh, numbins=numbins) # Stitch the 1D profiles back into serial data. gen1d.x = mpi.allreduce(gen1d.x, mpi.SUM) gen1d.m = mpi.allreduce(gen1d.m, mpi.SUM) gen1d.rho = mpi.allreduce(gen1d.rho, mpi.SUM) gen1d.H = mpi.allreduce(gen1d.H, mpi.SUM) n1d = len(gen1d.x) # Replicate the 1D slices into the full 2D data. self.x = [] self.y = [] self.m = [] self.rho = [] self.H = [] dy = (xmax[1] - xmin[1]) / ny hyinv = 1.0 / (nNodePerh * dy) for iy in xrange(ny): self.x += gen1d.x self.y += [xmin[1] + (iy + 0.5) * dy] * n1d self.m += [mi * (xmax[1] - xmin[1]) / ny for mi in gen1d.m] self.rho += gen1d.rho self.H += [SymTensor2d(H1d.xx, 0.0, 0.0, hyinv) for H1d in gen1d.H] # Have the base class break up the serial node distribution # for parallel cases. NodeGeneratorBase.__init__(self, True, self.x, self.y, self.m, self.rho, self.H) # If we're forcing round H tensors, do it. if SPH: self.makeHround() return
def latticeDistribution(self, nx, ny, rho, xmin=(0.0, 0.0), xmax=(1.0, 1.0), rmin=None, rmax=None, nNodePerh=2.01, xlmin=1e30, xlmax=-1e30): dx = (xmax[0] - xmin[0]) / nx dy = (xmax[1] - xmin[1]) / ny hx = 1.0 / (nNodePerh * dx) hy = 1.0 / (nNodePerh * dy) H0 = SymTensor2d(hx, 0.0, 0.0, hy) x = [] y = [] m = [] H = [] for j in xrange(ny): for i in xrange(nx): if i * dx < xlmin or i * dx > xlmax: xx = xmin[0] + (i + 0.5) * dx yy = xmin[1] + (j + 0.5) * dy r = sqrt(xx * xx + yy * yy) m0 = dx * dy * rho(Vector2d(xx, yy)) if ((r >= rmin or rmin is None) and (r <= rmax or rmax is None)): x.append(xx) y.append(yy) m.append(m0) H.append(H0) dx = dx * 0.5 dy = dy * 0.5 xx = xlmin + 0.5 * dx while (xx <= xlmax): for j in range(ny * 2): yy = xmin[1] + (j + 0.5) * dy r = sqrt(xx * xx + yy * yy) m0 = dx * dy * rho(Vector2d(xx, yy)) if ((r >= rmin or rmin is None) and (r <= rmax or rmax is None)): x.append(xx) y.append(yy) m.append(m0) H.append(H0) xx += dx return x, y, m, H
def __init__( self, boundary, # Some object that has "xmin", "xmax", & "contains" dx, # Nominal linear resolution rho, # initial mass density: constant, list, or function nNodePerh=2.01, # desired nPerh for H tensors jitter=0.0, # (fraction of dx) any randomness to initial positions SPH=False, # Should we force round H tensors? ): assert dx > 0.0 assert nNodePerh > 0.0 self.nNodePerh = nNodePerh # Start by clipping a lattice. xmin, xmax = boundary.xmin, boundary.xmax nx = max(1, int((xmax.x - xmin.x) / dx + 0.5)) ny = max(1, int((xmax.y - xmin.y) / dx + 0.5)) self.x, self.y = [], [] for iy in xrange(ny): for ix in xrange(nx): posi = Vector2d( xmin.x + (ix + 0.5 + jitter * rangen.uniform(0, 1)) * dx, xmin.y + (iy + 0.5 + jitter * rangen.uniform(0, 1)) * dx) if boundary.contains(posi): self.x.append(posi.x) self.y.append(posi.y) n = len(self.x) assert len(self.y) == n # Density and mass. if type(rho) is float: self.rho = ConstantRho(rho) else: self.rho = rho # Mass per node. self.m = [self.rho(Vector2d(self.x[i], self.y[i])) for i in xrange(n)] # Set H. h0 = nNodePerh * dx H0 = SymTensor2d(1.0 / h0, 0.0, 0.0, 1.0 / h0) self.H = [H0] * len(self.x) # Have the base class break up the serial node distribution # for parallel cases. NodeGeneratorBase.__init__(self, True, self.x, self.y, self.m, self.H) return
def latticeDistribution(self, nr, rho0, m0, xmin, xmax, rmax, nNodePerh = 2.01): assert nr > 0 assert rho0 > 0 nx = 2*nr+1 ny = 2*nr+1 dx = (xmax[0] - xmin[0])/nx dy = (xmax[1] - xmin[1])/ny n = nx*ny hx = 1.0/(nNodePerh*dx) hy = 1.0/(nNodePerh*dy) H0 = SymTensor2d(hx, 0.0, 0.0, hy) x = [] y = [] m = [] H = [] for j in xrange(ny): for i in xrange(nx): xx = xmin[0] + (i + 0.5)*dx yy = xmin[1] + (j + 0.5)*dy x.append(xx) y.append(yy) m.append(m0) H.append(H0) return x, y, m, H
def _string2SymTensor2d(x): return SymTensor2d(*tuple(x.split()))
def __init__(self, filename, materialName, nNodePerh=2.01, SPH=False, precision=20, Hscalefactor=1.0, extraFields=[], initializeBase=True, readFileToMemory=False, refineNodes=0): self.filename = filename self.nPerh = nNodePerh self.SPH = SPH self.extraFields = extraFields self.precision = "%" + "%i.%ie" % (precision + 3, precision) # For now we restrict to reading from a single (serial) file. allfiles = mpi.allreduce([filename], mpi.SUM) assert min([x == filename for x in allfiles]) self.serialfile = True # Open the file. if mpi.rank == 0: f = gzip.open(filename, "r") if readFileToMemory: self.f = f.readlines() f.close() else: self.f = f else: self.f = None # Read the positions. vals = readField2String(materialName, "positions", self.f) n = len(vals) self.x = [] self.y = [] for val in vals: x, y = tuple([float(x) for x in val.split()]) self.x.append(x) self.y.append(y) assert len(self.x) == n assert len(self.y) == n # Read the masses. vals = readField2String(materialName, "mass", self.f) assert len(vals) == n self.m = [float(x) for x in vals] assert len(self.m) == n # Read the mass densities. vals = readField2String(materialName, "density", self.f) assert len(vals) == n self.rho = [float(x) for x in vals] assert len(self.rho) == n # Read the velocities. vals = readField2String(materialName, "velocity", self.f) assert len(vals) == n self.vx = [] self.vy = [] for val in vals: vx, vy = tuple([float(x) for x in val.split()]) self.vx.append(vx) self.vy.append(vy) assert len(self.vx) == n assert len(self.vy) == n # Read the specific thermal energies. vals = readField2String(materialName, "specificThermalEnergy", self.f) assert len(vals) == n self.eps = [float(x) for x in vals] assert len(self.eps) == n # Read the H tensors. vals = readField2String(materialName, "Hinverse2", self.f) assert len(vals) == n self.H = [] for val in vals: Hi2 = SymTensor2d(*tuple([float(x) for x in val.split()])) * Hscalefactor H = Hi2.Inverse().sqrt() if SPH: hxy = sqrt(H.Determinant()) H = SymTensor2d.one * hxy self.H.append(H) assert len(self.H) == n # Read in any extra fields the user requested. # Note we make the assumption here that any extra fields are in fact scalar fields. for fname in extraFields: vals = readField2String(materialName, fname, self.f) assert len(vals) == n self.__dict__[fname] = [float(x) for x in vals] # Initialize the base class. if initializeBase: fields = tuple([ self.x, self.y, self.m, self.rho, self.vx, self.vy, self.eps, self.H ] + [self.__dict__[x] for x in extraFields]) NodeGeneratorBase.__init__(self, self.serialfile, *fields) if mpi.rank == 0: self.f.close() # Apply the requested number of refinements. for i in xrange(refineNodes): refineNodes2d(self) return
def __init__(self, nx, ny, rho, xmin, xmax, nNodePerh=2.01, SPH=False): assert nx > 0 assert ny > 0 assert len(xmin) == 2 assert len(xmax) == 2 assert xmax[0] >= xmin[0] assert xmax[1] >= xmin[1] assert nNodePerh > 0.0 # Remember the input. self.nx = nx self.ny = ny self.xmin = xmin self.xmax = xmax # If the user provided a constant for rho, then use the constantRho # class to provide this value. if type(rho) == type(1.0): self.rho = ConstantRho(rho) else: self.rho = rho # Compute the number of domains in each direction. lx = xmax[0] - xmin[0] ly = xmax[1] - xmin[1] nxdomains = int(sqrt(lx / ly * mpi.procs) + 0.1) nydomains = int(mpi.procs / nxdomains + 0.1) assert nxdomains * nydomains == mpi.procs # The number of nodes per domain. nxperdomain = nx / nxdomains nxremainder = nx % nxdomains nyperdomain = ny / nydomains nyremainder = ny % nydomains assert nxremainder < nxdomains assert nyremainder < nydomains # Compute the value for H. dx = lx / nx dy = ly / ny hx = nNodePerh * dx hy = nNodePerh * dy assert hx > 0.0 and hy > 0.0 H0 = SymTensor2d(1.0 / hx, 0.0, 0.0, 1.0 / hy) if SPH: hxy = sqrt(hx * hy) H0 = SymTensor2d.one / hxy # The mass per node. m0 = lx * ly * rho / (nx * ny) assert m0 > 0.0 # Compute our domain indicies. ixdomain = mpi.rank % nxdomains iydomain = mpi.rank / nxdomains ixmin = nodeindex(ixdomain, nxperdomain, nxremainder) ixmax = nodeindex(ixdomain + 1, nxperdomain, nxremainder) iymin = nodeindex(iydomain, nyperdomain, nyremainder) iymax = nodeindex(iydomain + 1, nyperdomain, nyremainder) assert ixmin < ixmax assert ixmin >= 0 and ixmax <= nx assert iymin < iymax assert iymin >= 0 and iymax <= ny # Now fill in the node values for this domain. self.x = [] self.y = [] self.m = [] self.H = [] for iy in xrange(iymin, iymax): for ix in xrange(ixmin, ixmax): self.x.append(xmin[0] + (ix + 0.5) * dx) self.y.append(xmin[1] + (iy + 0.5) * dy) self.m.append(m0) self.H.append(H0) assert mpi.allreduce(len(self.x), mpi.SUM) == nx * ny # Initialize the base class. NodeGeneratorBase.__init__(self, False) return
def refineNodes3d(gen, deta = 0.25): n = gen.localNumNodes() x = [] y = [] z = [] m = [] rho = [] vx = [] vy = [] vz = [] eps = [] H = [] extras = {} for name in gen.extraFields: extras[name] = [] etas = [Vector3d(-1, -1, -1) * deta, Vector3d(-1, -1, 1) * deta, Vector3d(-1, 1, -1) * deta, Vector3d(-1, 1, 1) * deta, Vector3d( 1, -1, -1) * deta, Vector3d( 1, -1, 1) * deta, Vector3d( 1, 1, -1) * deta, Vector3d( 1, 1, 1) * deta] for i in xrange(n): ri = gen.localPosition(i) mi = gen.localMass(i) Hi = gen.localHtensor(i) Hinv = Hi.Inverse() eigen = Hinv.eigenVectors() R = rotationMatrix3d(eigen.eigenVectors.getColumn(0)) T = SymTensor2d(eigen.eigenValues.x, 0.0, 0.0, 0.0, eigen.eigenValues.y, 0.0, 0.0, 0.0, eigen.eigenValues.z) T.rotationalTransform(eigen.eigenVectors) T = T*R.Inverse() mj = mi/8.0 Hj = Hi*2.0 for eta in etas: rj = ri + T*eta x.append(rj.x) y.append(rj.y) z.append(rj.z) m.append(mj) rho.append(gen.rho[i]) vx.append(gen.vx[i]) vy.append(gen.vy[i]) vz.append(gen.vz[i]) eps.append(gen.eps[i]) H.append(Hj) for name in gen.extraFields: extras[name].append(gen.__dict__[name][i]) gen.x = x gen.y = y gen.z = z gen.m = m gen.rho = rho gen.vx = vx gen.vy = vy gen.vz = vz gen.eps = eps gen.H = H for name in gen.extraFields: gen.__dict__[name] = extras[name] for f in ([gen.x, gen.y, gen.z, gen.m, gen.vx, gen.vy, gen.vz, gen.eps, gen.H] + [gen.__dict__[x] for x in gen.extraFields]): assert len(f) == len(etas)*n return
def __init__(self, drCenter, drRatio, rho, rmin, rmax, startFromCenter = True, thetamin = 0.0, thetamax = 0.5*pi, phi = pi, ntheta = 1, center = (0.0, 0.0, 0.0), distributionType = "constantDTheta", # one of (constantDTheta, constantNTheta) aspectRatio = 1.0, # only for constantDTheta nNodePerh = 2.01, SPH = False, rejecter = None): assert thetamax <= pi self.gen2d = GenerateRatioSphere2d(drStart = drCenter, drRatio = drRatio, rho = rho, rmin = rmin, rmax = rmax, startFromCenter = startFromCenter, thetamin = thetamin, thetamax = thetamax, ntheta = ntheta, center = (0.0, 0.0), distributionType = distributionType, aspectRatio = aspectRatio, nNodePerh = nNodePerh, SPH = SPH) # The 2D class already split the nodes up between processors, but # we want to handle that ourselves. Distribute the full set of RZ # nodes to every process, then redecompose them below. self.x = mpi.allreduce(self.gen2d.x[:], mpi.SUM) self.y = mpi.allreduce(self.gen2d.y[:], mpi.SUM) self.m = mpi.allreduce(self.gen2d.m[:], mpi.SUM) self.H = mpi.allreduce(self.gen2d.H[:], mpi.SUM) n = len(self.x) self.z = [0.0]*n self.globalIDs = [0]*n # Convert the 2-D H tensors to 3-D, and correct the masses. for i in xrange(n): xi = self.x[i] yi = self.y[i] H2d = SymTensor2d(self.H[i]) H2dinv = H2d.Inverse() hxy0 = 0.5*(H2dinv.Trace()) dphi = CylindricalBoundary.angularSpacing(yi, hxy0, nNodePerh, 2.0) assert dphi > 0.0 nsegment = max(1, int(phi/dphi + 0.5)) dphi = phi/nsegment hz = dphi*yi*nNodePerh self.H[i] = SymTensor3d(H2d.xx, H2d.xy, 0.0, H2d.yx, H2d.yy, 0.0, 0.0, 0.0, 1.0/hz) if SPH: h0 = self.H[i].Determinant()**(1.0/3.0) self.H[-1] = SymTensor3d(h0, 0.0, 0.0, 0.0, h0, 0.0, 0.0, 0.0, h0) # Convert the mass to the full hoop mass, which will then be used in # generateCylDistributionFromRZ to compute the actual nodal masses. mi = self.m[i] circ = 2.0*pi*yi mhoop = mi*circ self.m[i] = mhoop assert len(self.m) == n assert len(self.H) == n # Duplicate the nodes from the xy-plane, creating rings of nodes about # the x-axis. We use a C++ helper method for the sake of speed. kernelExtent = 2.0 extras = [] xvec = self.vectorFromList(self.x, vector_of_double) yvec = self.vectorFromList(self.y, vector_of_double) zvec = self.vectorFromList(self.z, vector_of_double) mvec = self.vectorFromList(self.m, vector_of_double) Hvec = self.vectorFromList(self.H, vector_of_SymTensor3d) globalIDsvec = self.vectorFromList(self.globalIDs, vector_of_int) extrasVec = vector_of_vector_of_double() for extra in extras: extrasVec.append(self.vectorFromList(extra, vector_of_double)) generateCylDistributionFromRZ(xvec, yvec, zvec, mvec, Hvec, globalIDsvec, extrasVec, nNodePerh, kernelExtent, phi, mpi.rank, mpi.procs) self.x = [x + center[0] for x in xvec] self.y = [x + center[1] for x in yvec] self.z = [z + center[2] for z in zvec] self.m = list(mvec) self.H = [SymTensor3d(x) for x in Hvec] self.globalIDs = list(globalIDsvec) for i in xrange(len(extras)): extras[i] = list(extrasVec[i]) self.center = Vector2d(*center) # If the user provided a "rejecter", give it a pass # at the nodes. if rejecter: self.x, self.y, self.z, self.m, self.H = rejecter(self.x, self.y, self.z, self.m, self.H) # Initialize the base class. NodeGeneratorBase.__init__(self, False, self.x, self.y, self.z, self.m, self.H) return
def __init__(self, drStart, drRatio, rho, rmin, rmax, startFromCenter = True, thetamin = 0.0, thetamax = 0.5*pi, ntheta = 1, center = (0.0, 0.0), distributionType = "constantDTheta", # one of (constantDTheta, constantNTheta) aspectRatio = 1.0, # only for constantDTheta nNodePerh = 2.01, SPH = False, rejecter = None, perturbFunc = None): assert drStart > 0.0 assert drRatio > 0.0 assert nNodePerh > 0.0 assert rmin >= 0.0 assert rmax > rmin assert thetamax > thetamin assert distributionType.lower() in ("constantdtheta", "constantntheta") self.center = center # Did we get passed a function or a constant for the density? if type(rho) == type(1.0): def rhofunc(posi): return rho else: rhofunc = rho self.rhofunc = rhofunc # Do we have a perturbation function? def zeroPerturbation(posi): return posi if not perturbFunc: perturbFunc = zeroPerturbation self.x, self.y, self.m, self.H = [], [], [], [] constantN = (distributionType.lower() == "constantntheta") Dtheta = thetamax - thetamin nthetamin = max(2, int(Dtheta/(0.5*pi) + 0.5)*2) # Decide the actual drStart we're going to use to arrive at an integer number of radial bins. if abs(drRatio - 1.0) > 1e-4: neff = max(1, int(log(1.0 - (rmax - rmin)*(1.0 - drRatio)/drStart)/log(drRatio) + 0.5)) drStart = (rmax - rmin)*(1.0 - drRatio)/(1.0 - drRatio**neff) else: neff = max(1, int((rmax - rmin)/drStart + 0.5)) drStart = (rmax - rmin)/neff print "Adjusting initial radial spacing to %g in order to create an integer radial number of bins %i." % (drStart, neff) # Step in radius (in or out) until we span the full radial range. dr = drStart for i in xrange(neff): if abs(drRatio - 1.0) > 1e-4: if startFromCenter: r0 = min(rmax, rmin + drStart*(1.0 - drRatio**i)/(1.0 - drRatio)) r1 = min(rmax, rmin + drStart*(1.0 - drRatio**(i + 1))/(1.0 - drRatio)) else: r0 = max(rmin, rmax - drStart*(1.0 - drRatio**(i + 1))/(1.0 - drRatio)) r1 = max(rmin, rmax - drStart*(1.0 - drRatio**i)/(1.0 - drRatio)) else: r0 = min(rmax, rmin + i*drStart) r1 = min(rmax, rmin + (i + 1)*drStart) dr = r1 - r0 ri = 0.5*(r0 + r1) li = Dtheta*ri if constantN: ntheta = ntheta else: ntheta = max(nthetamin, int(li/dr*aspectRatio)) dtheta = Dtheta/ntheta hr = nNodePerh * dr ha = nNodePerh * ri*dtheta for j in xrange(ntheta): theta0 = thetamin + j*dtheta theta1 = thetamin + (j + 1)*dtheta pos0 = perturbFunc(Vector2d(r0*cos(theta0), r0*sin(theta0))) pos1 = perturbFunc(Vector2d(r1*cos(theta0), r1*sin(theta0))) pos2 = perturbFunc(Vector2d(r1*cos(theta1), r1*sin(theta1))) pos3 = perturbFunc(Vector2d(r0*cos(theta1), r0*sin(theta1))) areai = 0.5*((pos1 - pos0).cross(pos2 - pos0).z + (pos2 - pos0).cross(pos3 - pos0).z) posi = 0.25*(pos0 + pos1 + pos2 + pos3) mi = areai*self.rhofunc(posi) xi = posi.x yi = posi.y self.x.append(xi + center[0]) self.y.append(yi + center[1]) self.m.append(mi) if SPH: hi = sqrt(hr*ha) self.H.append(SymTensor2d(1.0/hi, 0.0, 0.0, 1.0/hi)) else: self.H.append(SymTensor2d(1.0/hr, 0.0, 0.0, 1.0/ha)) runit = Vector2d(xi, yi).unitVector() T = rotationMatrix2d(runit).Transpose() self.H[-1].rotationalTransform(T) # # Do a numerical integral to get the expected total mass. # class integfunc(ScalarFunctor): # def __init__(self, rho, Dtheta): # ScalarFunctor.__init__(self) # self.rho = rho # self.Dtheta = Dtheta # return # def __call__(self, ri): # return Dtheta*ri*self.rho(ri) # M1 = simpsonsIntegrationDouble(integfunc(rhofunc, Dtheta), rmin, rmax, 10000) # # Make sure the total mass is what we intend it to be, by applying # # a multiplier to the particle masses. # M0 = sum(self.m) # assert M0 > 0.0 # massCorrection = M1/M0 # for i in xrange(len(self.m)): # self.m[i] *= massCorrection # print "Applied a mass correction of %f to ensure total mass is %f." % (massCorrection, M1) # If the user provided a "rejecter", give it a pass # at the nodes. if rejecter: self.x, self.y, self.m, self.H = rejecter(self.x, self.y, self.m, self.H) # Have the base class break up the serial node distribution # for parallel cases. NodeGeneratorBase.__init__(self, True, self.x, self.y, self.m, self.H) return
def __init__(self, n, rho, boundary, holes=[], maxIterations=100, fracTol=1.0e-3, tessellationFileName=None, nNodePerh=2.01, offset=(0.0, 0.0), rejecter=None): assert n > 0 # Did we get passed a function or a constant for the density? if type(rho) == type(1.0): def rhofunc(posi): return rho else: rhofunc = rho self.rhofunc = rhofunc # Build a polytope PLC version of the boundary. plc = poly.polytope.PLC2d() plc_coords = poly.vector_of_double() edges = boundary.edges vertices = boundary.vertices() plc.facets.resize(len(edges)) for i, edge in enumerate(edges): plc.facets[i].append(edge.first) plc.facets[i].append(edge.second) assert len(plc.facets[i]) == 2 for p in vertices: plc_coords.append(p[0]) plc_coords.append(p[1]) assert len(plc_coords) == 2 * len(vertices) # Add any holes to the boundary PLC. plc.holes.resize(len(holes)) for ihole, hole in enumerate(holes): offlast = len(plc_coords) / 2 edges = hole.edges vertices = hole.vertices() plc.holes[ihole].resize(len(edges)) for i, edge in enumerate(edges): plc.holes[ihole][i].append(offlast + edge.first) plc.holes[ihole][i].append(offlast + edge.second) assert len(plc.holes[ihole][i]) == 2 for p in vertices: plc_coords.append(p[0]) plc_coords.append(p[1]) assert len(plc_coords) % 2 == 0 # Initialize the desired number of generators in the boundary using the Sobol sequence. generators = poly.vector_of_double() seed = 0 length = max(boundary.xmax.x - boundary.xmin.x, boundary.xmax.y - boundary.xmin.y) while len(generators) < 2 * n: [coords, seed] = i4_sobol(2, seed) p = boundary.xmin + length * Vector2d(coords[0], coords[1]) ihole = 0 use = boundary.contains(p, False) if use: while use and ihole < len(holes): use = not holes[ihole].contains(p, False) ihole += 1 if use: generators.append(p.x) generators.append(p.y) assert len(generators) == 2 * n # Iterate the points toward centroidal relaxation. self.tessellation = poly.polytope.Tessellation2d() tessellator = poly.polytope.BoostTessellator2d() iteration = 0 maxDelta = 2.0 * fracTol while iteration < maxIterations and maxDelta > fracTol: tessellator.tessellate(points=generators, PLCpoints=plc_coords, geometry=plc, mesh=self.tessellation) new_generators = self.computeWeightedCentroids(self.tessellation) assert len(new_generators) == len(generators) maxDelta = 0.0 for i in xrange(len(generators) / 2): deltai = sqrt((generators[2 * i] - new_generators[2 * i])**2 + (generators[2 * i + 1] - new_generators[2 * i + 1])**2) maxDelta = max(maxDelta, deltai / length) generators[2 * i] = 0.5 * (generators[2 * i] + new_generators[2 * i]) generators[2 * i + 1] = 0.5 * (generators[2 * i + 1] + new_generators[2 * i + 1]) iteration += 1 print "CentroidalGenerator2d: Iteration %i, maxDelta=%g" % ( iteration, maxDelta) # If requested, write out the final tessellation to a silo file. if tessellationFileName: poly.polytope.writeTessellation2d(mesh=self.tessellation, filePrefix=tessellationFileName, nodeFields=None, edgeFields=None, faceFields=None, cellFields=None, cycle=iteration) # Now we can fill out the usual Spheral generator info. assert len(self.tessellation.cells) == n self.x, self.y, self.m, self.H = [], [], [], [] centroids = self.computeWeightedCentroids(self.tessellation) masses = self.computeMasses(self.tessellation) areas = self.computeAreas(self.tessellation) assert len(centroids) == 2 * n assert len(masses) == n assert len(areas) == n for i in xrange(n): self.x.append(centroids[2 * i] + offset[0]) self.y.append(centroids[2 * i + 1] + offset[1]) self.m.append(masses[i]) hi = nNodePerh * sqrt(areas[i] / pi) assert hi > 0.0 self.H.append(SymTensor2d(1.0 / hi, 0.0, 0.0, 1.0 / hi)) assert len(self.x) == n assert len(self.y) == n assert len(self.m) == n assert len(self.H) == n # If the user provided a "rejecter", give it a pass # at the nodes. if rejecter: self.x, self.y, self.m, self.H = rejecter(self.x, self.y, self.m, self.H) # Have the base class break up the serial node distribution # for parallel cases. NodeGeneratorBase.__init__(self, True, self.x, self.y, self.m, self.H) return
def latticeCylindricalDistribution(self, nx, ny, rho, xmin=(0.0, 0.0), xmax=(1.0, 1.0), rmin=None, rmax=None, nNodePerh=2.01): k = 0 np = 0 deltar = rmax - rmin dx = (xmax[0] - xmin[0]) / nx dy = (xmax[1] - xmin[1]) / ny hx = 1.0 / (nNodePerh * dx) hy = 1.0 / (nNodePerh * dy) H0 = SymTensor2d(hx, 0.0, 0.0, hy) x = [] y = [] m = [] H = [] ml = [] Hl = [] xl = [] yl = [] xc = [] yc = [] mc = [] Hc = [] for j in xrange(ny): for i in xrange(nx): xx = xmin[0] + (i + 0.5) * dx yy = xmin[1] + (j + 0.5) * dy r = sqrt(xx * xx + yy * yy) m0 = dx * dy * rho(Vector2d(xx, yy)) if (r >= rmin * 0.8): xl.append(xx) yl.append(yy) ml.append(m0) Hl.append(H0) k = k + 1 if (r >= rmax): x.append(xx) y.append(yy) m.append(m0) H.append(H0) np = np + 1 # Start at the outermost radius, and work our way inward. theta = 2 * 3.14159 ri = rmax + 2.0 * nNodePerh / nx #import random #random.seed() while ri > 0: # Get the nominal delta r, delta theta, number of nodes, and mass per # node at this radius. rhoi = rho(Vector2d(ri, 0.0)) dr = sqrt(m0 / rhoi) arclength = theta * ri arcmass = arclength * dr * rhoi nTheta = max(1, int(arcmass / m0)) dTheta = theta / nTheta mi = arcmass / nTheta hi = nNodePerh * 0.5 * (dr + ri * dTheta) Hi = SymTensor2d(1.0 / hi, 0.0, 0.0, 1.0 / hi) # Now assign the nodes for this radius. for i in xrange(nTheta): thetai = (i + 0.5) * dTheta xc.append(ri * cos(thetai)) yc.append(ri * sin(thetai)) mc.append(mi) Hc.append(Hi) xi = ri * cos(thetai) yi = ri * sin(thetai) if (ri < rmin): x.append(xi) y.append(yi) m.append(mi) H.append(Hi) np = np + 1 elif (ri >= rmin): #eps = random.random() #func = ((ri-rmin)/deltar)**2 func = 1 - ri / (rmin - rmax) - rmax / (rmax - rmin) if (func > 1.0): func = 1.0 if (func < 0.0): func = 0.0 #if (eps <= func): #x.append(ri*cos(thetai)) #y.append(ri*sin(thetai)) #m.append(mi) #H.append(Hi) #else: minddr = nx mink = 2 * k for j in xrange(k): ddr = sqrt((xl[j] - xi)**2 + (yl[j] - yi)**2) if (ddr < minddr): minddr = ddr mink = j xi = xi + (xl[mink] - xi) * func yi = yi + (yl[mink] - yi) * func minddr = nx for j in xrange(np): ddr = sqrt((x[j] - xi)**2 + (y[j] - yi)**2) if (ddr < minddr): minddr = ddr if (minddr > (1.0 / hx) * 0.5): x.append(xi + (xl[mink] - xi) * func) y.append(yi + (yl[mink] - yi) * func) m.append(ml[mink]) H.append(Hl[mink]) # Decrement to the next radial bin inward. ri = max(0.0, ri - dr) return x, y, m, H
def __init__(self, dxSurface, xratio, dySurface, yratio, rho, xmin, xmax, nNodePerh=2.01, SPH=False, flipx=False, flipy=False): assert dxSurface > 0.0 assert xratio > 0.0 assert dySurface > 0.0 assert yratio > 0.0 assert xmin[0] < xmax[0] assert xmin[1] < xmax[1] assert nNodePerh > 0.0 # If the user provided a constant for rho, then use the constantRho # class to provide this value. if type(rho) == type(1.0): self.rhofunc = ConstantRho(rho) else: self.rhofunc = rho self.x, self.y, self.m, self.H, self.rho = [], [], [], [], [] # Decide the actual ratios we're going to use to arrive at an integer number of radial bins. def adjustRatio(drStart, drRatio, rmin, rmax): if abs(drRatio - 1.0) > 1e-4: neff = max( 1, int( log(1.0 - (rmax - rmin) * (1.0 - drRatio) / drStart) / log(drRatio) + 0.5)) drStart = (rmax - rmin) * (1.0 - drRatio) / (1.0 - drRatio**neff) else: neff = max(1, int((rmax - rmin) / drStart + 0.5)) drStart = (rmax - rmin) / neff return drStart, neff dxSurface, nxeff = adjustRatio(dxSurface, xratio, xmin[0], xmax[0]) dySurface, nyeff = adjustRatio(dySurface, yratio, xmin[1], xmax[1]) print "Adjusting initial spacing to (%g, %g) in order to create integer numbers of bins (%i, %i) to edges." % ( dxSurface, dySurface, nxeff, nyeff) def flipcoord(xi, x0, x1): return x0 + x1 - xi # Work our way in from the y surface. y1 = xmax[1] dy = dySurface while y1 > xmin[1] + 0.1 * dy: y0 = max(xmin[1], y1 - dy) yi = 0.5 * (y0 + y1) hy = nNodePerh * dy yi0 = y0 yi1 = y1 if flipy: yi = flipcoord(yi, xmin[1], xmax[1]) yi0 = flipcoord(yi0, xmin[1], xmax[1]) yi1 = flipcoord(yi1, xmin[1], xmax[1]) # Work our way in from the x surface. x1 = xmax[0] dx = dxSurface while x1 > xmin[0] + 0.1 * dx: x0 = max(xmin[0], x1 - dx) xi = 0.5 * (x0 + x1) hx = nNodePerh * dx xi0 = x0 xi1 = x1 if flipx: xi = flipcoord(xi, xmin[0], xmax[0]) xi0 = flipcoord(xi0, xmin[0], xmax[0]) xi1 = flipcoord(xi1, xmin[0], xmax[0]) self.x.append(xi) self.y.append(yi) rhoi, mi = computeRhoAndMass2d(Vector2d(xi0, yi0), Vector2d(xi1, yi0), Vector2d(xi1, yi1), Vector2d(xi0, yi1), Vector2d(xi, yi), self.rhofunc) self.m.append(mi) self.rho.append(rhoi) self.H.append(SymTensor2d(1.0 / hx, 0.0, 0.0, 1.0 / hy)) x1 = x0 dx *= xratio y1 = y0 dy *= yratio # # Make sure the total mass is what we intend it to be, by applying # # a multiplier to the particle masses. # M0 = 0.0 # for m in self.m: # M0 += m # assert M0 > 0.0 # M1 = (xmax[0] - xmin[0]) * (xmax[1] - xmin[1]) * rho # massCorrection = M1/M0 # for i in xrange(len(self.m)): # self.m[i] *= massCorrection # print "Applied a mass correction of %f to ensure total mass is %f." % (massCorrection, M1) # Have the base class break up the serial node distribution # for parallel cases. NodeGeneratorBase.__init__(self, True, self.x, self.y, self.m, self.H, self.rho) return
def __init__(self, dxSurface, xratio, dySurface, yratio, rho, xmin, xmax, nNodePerh=2.01, SPH=False, flipx=False, flipy=False): assert dxSurface > 0.0 assert xratio > 0.0 assert dySurface > 0.0 assert yratio > 0.0 assert xmin[0] < xmax[0] assert xmin[1] < xmax[1] assert nNodePerh > 0.0 self.rho = rho self.x, self.y, self.m, self.H = [], [], [], [] # Work our way in from the y surface. y1 = xmax[1] dy = dySurface while y1 > xmin[1]: y0 = max(xmin[1], y1 - dy) yi = 0.5 * (y0 + y1) hy = nNodePerh * dy if flipy: yi = xmin[1] + xmax[1] - yi # Work our way in from the x surface. x1 = xmax[0] dx = dxSurface while x1 > xmin[0]: x0 = max(xmin[0], x1 - dx) xi = 0.5 * (x0 + x1) hx = nNodePerh * dx if flipx: xi = xmin[0] + xmax[0] - xi self.x.append(xi) self.y.append(yi) self.m.append(rho * dx * dy) self.H.append(SymTensor2d(1.0 / hx, 0.0, 0.0, 1.0 / hy)) x1 = x0 dx *= xratio y1 = y0 dy *= yratio # Make sure the total mass is what we intend it to be, by applying # a multiplier to the particle masses. M0 = 0.0 for m in self.m: M0 += m assert M0 > 0.0 M1 = (xmax[0] - xmin[0]) * (xmax[1] - xmin[1]) * rho massCorrection = M1 / M0 for i in xrange(len(self.m)): self.m[i] *= massCorrection print "Applied a mass correction of %f to ensure total mass is %f." % ( massCorrection, M1) # Have the base class break up the serial node distribution # for parallel cases. NodeGeneratorBase.__init__(self, True, self.x, self.y, self.m, self.H) return