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
Beispiel #2
0
    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 computeWeightedCentroids(self, tessellation):
     barycenters = self.computeBarycenters(tessellation)
     centroids = []
     for icell, cell in enumerate(tessellation.cells):
         mass = 0.0
         xc = 0.0
         yc = 0.0
         for ftmp in cell:
             if ftmp < 0:
                 n0 = tessellation.faces[~ftmp][1]
                 n1 = tessellation.faces[~ftmp][0]
             else:
                 n0 = tessellation.faces[ftmp][0]
                 n1 = tessellation.faces[ftmp][1]
             x0 = tessellation.nodes[2 * n0]
             y0 = tessellation.nodes[2 * n0 + 1]
             x1 = tessellation.nodes[2 * n1]
             y1 = tessellation.nodes[2 * n1 + 1]
             xb = barycenters[icell][0]
             yb = barycenters[icell][1]
             xt = (x0 + x1 + xb) / 3.0
             yt = (y0 + y1 + yb) / 3.0
             xe = (x0 + x1) / 2.0
             ye = (y0 + y1) / 2.0
             d = 0.5 * ((x0 - xb) * (y1 - yb) - (x1 - xb) *
                        (y0 - yb)) * self.rhofunc(Vector2d(xe, ye))
             mass += d
             xc += d * xt
             yc += d * yt
         xc /= mass
         yc /= mass
         centroids.append(xc)
         centroids.append(yc)
     return centroids
 def computeMasses(self, tessellation):
     barycenters = self.computeBarycenters(tessellation)
     result = []
     for icell, cell in enumerate(tessellation.cells):
         mass = 0.0
         for ftmp in cell:
             if ftmp < 0:
                 n0 = tessellation.faces[~ftmp][1]
                 n1 = tessellation.faces[~ftmp][0]
             else:
                 n0 = tessellation.faces[ftmp][0]
                 n1 = tessellation.faces[ftmp][1]
             x0 = tessellation.nodes[2 * n0]
             y0 = tessellation.nodes[2 * n0 + 1]
             x1 = tessellation.nodes[2 * n1]
             y1 = tessellation.nodes[2 * n1 + 1]
             xb = barycenters[icell][0]
             yb = barycenters[icell][1]
             xt = (x0 + x1 + xb) / 3.0
             yt = (y0 + y1 + yb) / 3.0
             xe = (x0 + x1) / 2.0
             ye = (y0 + y1) / 2.0
             d = 0.5 * ((x0 - xb) * (y1 - yb) - (x1 - xb) *
                        (y0 - yb)) * self.rhofunc(Vector2d(xe, ye))
             mass += d
         result.append(mass)
     return result
def _string2Vector2d(x):
    return Vector2d(*tuple(x.split()))
Beispiel #6
0
    def __init__(self, nr, densityProfileMethod,
                 rmin = 0.0,
                 rmax = 1.0,
                 thetaMin = 0.0,
                 thetaMax = pi,
                 nNodePerh = 2.01,
                 offset=None,
                 m0ForMassMatching=None):
        
        assert nr > 0
        assert rmin >= 0
        assert rmin < rmax
        assert thetaMin < thetaMax
        assert thetaMin >= 0.0 and thetaMin <= 2.0*pi
        assert thetaMax >= 0.0 and thetaMax <= 2.0*pi
        assert nNodePerh > 0.0
        assert offset is None or len(offset)==3
        
        if offset is None:
            self.offset = Vector2d(0,0)
        else:
            self.offset = Vector2d(offset[0],offset[1])
        
        self.nr         = nr
        self.rmin       = rmin
        self.rmax       = rmax
        self.thetaMin   = thetaMin
        self.thetaMax   = thetaMax
        self.nNodePerh  = nNodePerh
        
        self.xmin       = Vector2d(-2.0*rmax,-2.0*rmax)
        self.xmax       = Vector2d(2.0*rmax,2.0*rmax)
        
        # no reason to support a constant density method here, just use a regular lattice for that
        self.densityProfileMethod = densityProfileMethod
        
        # Determine how much total mass there is in the system.
        targetMass = self.integrateTotalMass(self.densityProfileMethod,
                                                 rmin, rmax,
                                                 thetaMin, thetaMax)

        
        #targetMass = self.integrateTotalMass(self.densityProfileMethod,
        #                                         rmax)
        
        targetN         = pi*(nr**2)
        self.m0         = targetMass/targetN
        self.vol        = pi*(rmax**2)
        # what this means is this currently only supports creating a full sphere and then
        # cutting out the middle to rmin if rmin > 0
        
        if m0ForMassMatching is None:
            self.rho0       = targetMass/self.vol
        else:
            self.m0 = m0ForMassMatching
            self.rho0 = targetN*self.m0/self.vol
        
        print "Found total mass = {0:3.3e} with rho0 = {1:3.3e}".format(targetMass,self.rho0)
    
        # compute kappa first
        # k = 3/(self.rho0*rmax**3) * targetMass/(4.0*pi)
        # print "Found kappa={0:3.3f}. Was that what you expected?".format(k)
        
        nlat = nr
        
        # create the unstretched lattice
        self.xl, self.yl, self.ml, self.Hl = \
            self.latticeDistribution(nlat,
                                     self.rho0,
                                     self.m0,
                                     self.xmin,    # (xmin, ymin, zmin)
                                     self.xmax,    # (xmax, ymax, zmax)
                                     self.rmax,
                                     self.nNodePerh)


        self.rl = []
        for i in xrange(len(self.xl)):
            self.rl.append(sqrt(self.xl[i]**2+self.yl[i]**2))
        
        print "Sorting unstretched lattice... %d elements" % len(self.rl)
        
        multiSort(self.rl,self.xl,self.yl)
        
        self.x = []
        self.y = []
        self.m = []
        self.H = []
        
        nx  = 2*nlat+1
        eta = (self.xmax[0] - self.xmin[0])/nx
        
        print "Stretching lattice..."

        dr  = eta * 0.01    # this will essentially be the error in the new dumb way
        r0p = 0
        rp  = 0
        rn  = 0
        for i in xrange(1,len(self.rl)):
            #print "%d / %d" % (i,len(self.rl))
            r0 = self.rl[i]
            if (abs(r0-r0p)/r0>1e-10):
                sol     = r0**2*self.rho0/2.0
                iter    = int(10*rmax // dr)
                fn      = 0
                for j in xrange(iter+1):
                    rj  = dr*j
                    rjj = dr*(j+1)
                    fj  = rj * densityProfileMethod(rj)
                    fjj = rjj * densityProfileMethod(rjj)
                    fn  = fn + 0.5*(fj+fjj)*dr
                    if (fn>=sol):
                        rn = rj
                        break
            r0p = r0
            if (rn <= rmax and rn > rmin):
                self.x.append(self.xl[i] * rn/r0)
                self.y.append(self.yl[i] * rn/r0)
                self.m.append(self.ml[i])
                self.H.append(self.Hl[i])
    
        seededMass = sum(self.m)
        
        mAdj = targetMass / seededMass
        for i in xrange(len(self.m)):
            self.m[i] = self.m[i] * mAdj

        
        # Initialize the base class.  If "serialInitialization" is True, this
        # is where the points are broken up between processors as well.
        serialInitialization = True
        NodeGeneratorBase.__init__(self, serialInitialization,
                                   self.x, self.y, self.m, self.H)
    
        return
Beispiel #7
0
 def localMassDensity(self, i):
     loc = Vector2d(0,0)
     loc = self.localPosition(i) - self.offset
     return self.densityProfileMethod(loc.magnitude())
Beispiel #8
0
def refineNodes2d(gen,
                  deta = 0.25):
    n = gen.localNumNodes()
    x = []
    y = []
    m = []
    rho = []
    vx = []
    vy = []
    eps = []
    H = []
    extras = {}
    for name in gen.extraFields:
        extras[name] = []

    etas = [Vector2d(-1, -1) * deta,
            Vector2d(-1,  1) * deta,
            Vector2d( 1, -1) * deta,
            Vector2d( 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 = rotationMatrix2d(eigen.eigenVectors.getColumn(0))
        T = SymTensor2d(eigen.eigenValues.x, 0.0,
                        0.0,                 eigen.eigenValues.y)
        T.rotationalTransform(eigen.eigenVectors)
        T = T*R.Inverse()

        mj = 0.25*mi
        Hj = Hi*2.0
        for eta in etas:
            rj = ri + T*eta
            x.append(rj.x)
            y.append(rj.y)
            m.append(mj)
            rho.append(gen.rho[i])
            vx.append(gen.vx[i])
            vy.append(gen.vy[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.m = m
    gen.rho = rho
    gen.vx = vx
    gen.vy = vy
    gen.eps = eps
    gen.H = H
    for name in gen.extraFields:
        gen.__dict__[name] = extras[name]

    for f in ([gen.x, gen.y, gen.m, gen.vx, gen.vy, gen.eps, gen.H] +
              [gen.__dict__[x] for x in gen.extraFields]):
        assert len(f) == len(etas)*n
    return
 def localMassDensity(self, i):
     return self.gen2d.rhofunc((Vector2d(self.x[i], self.y[i]) - self.center).magnitude())
Beispiel #10
0
    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
Beispiel #11
0
 def localMassDensity(self, i):
     return self.rho(Vector2d(self.x[i], self.y[i]))
    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,
                 nRadial,
                 nTheta,
                 rho,
                 xmin=None,
                 xmax=None,
                 rmin=None,
                 rmax=None,
                 nNodePerh=2.01,
                 theta=pi / 2.0,
                 azimuthalOffsetFraction=0.0,
                 SPH=False,
                 rotation=0.0,
                 offset=None,
                 xminreject=None,
                 xmaxreject=None,
                 rreject=None,
                 originreject=None,
                 reversereject=False,
                 relaxation=None,
                 rejecter=None,
                 xlmin=None,
                 xlmax=None):

        assert nRadial > 0
        assert nTheta > 0
        assert nNodePerh > 0.0
        assert offset is None or len(offset) == 2
        assert ((xminreject is None and xmaxreject is None)
                or (len(xminreject) == 2 and len(xmaxreject) == 2))
        assert ((rreject is None and originreject is None)
                or (rreject and len(originreject) == 2))
        assert azimuthalOffsetFraction == 0.0 or theta == 2.0 * pi

        self.nRadial = nRadial
        self.nTheta = nTheta
        self.xmin = xmin
        self.xmax = xmax
        self.rmin = rmin
        self.rmax = rmax
        self.nNodePerh = nNodePerh
        self.theta = theta
        self.azimuthalOffsetFraction = azimuthalOffsetFraction
        self.xlmin = xlmin
        self.xlmax = xlmax

        # 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


        self.x, self.y, self.m, self.H = \
                self.latticeDistribution(self.nRadial, # nx
                                         self.nTheta,  # ny
                                         self.rho,
                                         self.xmin,
                                         self.xmax,
                                         self.rmin,
                                         self.rmax,
                                         self.nNodePerh,
                                         self.xlmin,
                                         self.xlmax)

        # If SPH has been specified, make sure the H tensors are round.
        if SPH:
            self.makeHround()

        # If requested, apply a rotation (in radians).
        if rotation:
            nhat = Vector2d(cos(rotation), -sin(rotation))
            R = rotationMatrix2d(nhat)
            for i in xrange(len(self.x)):
                v = R * Vector2d(self.x[i], self.y[i])
                self.x[i], self.y[i] = v.x, v.y
                self.H[i].rotationalTransform(R)

        # If requested, shift the nodes.
        if offset:
            for i in xrange(len(self.x)):
                self.x[i] += offset[0]
                self.y[i] += offset[1]

        # Did the user request to reject nodes based on a box?
        if xminreject:
            assert len(xminreject) == 2 and len(xmaxreject) == 2
            xminreject = Vector2d(*xminreject)
            xmaxreject = Vector2d(*xmaxreject)
            x = []
            y = []
            m = []
            H = []
            for xi, yi, mi, Hi in zip(self.x, self.y, self.m, self.H):
                t = testPointInBox2d(Vector2d(xi, yi), xminreject, xmaxreject)
                if ((t and (not reversereject))
                        or ((not t) and reversereject)):
                    x.append(xi)
                    y.append(yi)
                    m.append(mi)
                    H.append(Hi)
            self.x = x
            self.y = y
            self.m = m
            self.H = H

        # Did the user request to reverse nodes based on a cylinder?
        if rreject:
            assert len(originreject) == 2
            originreject = Vector2d(*originreject)
            x = []
            y = []
            m = []
            H = []
            for xi, yi, mi, Hi in zip(self.x, self.y, self.m, self.H):
                t = (Vector2d(xi, yi) - originreject).magnitude() <= rreject
                if ((t and (not reversereject))
                        or ((not t) and reversereject)):
                    x.append(xi)
                    y.append(yi)
                    m.append(mi)
                    H.append(Hi)
            self.x = x
            self.y = y
            self.m = m
            self.H = H

        # 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)

        # If requested, employ some relaxation on the NodeDistribution.
        if relaxation:
            relaxation(self)

        return
Beispiel #15
0
    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
Beispiel #16
0
 def localPosition(self, i):
     assert i >= 0 and i < len(self.x)
     assert len(self.x) == len(self.y)
     return Vector2d(self.x[i], self.y[i])
Beispiel #17
0
 def localMassDensity(self, i):
     assert i >= 0 and i < len(self.H)
     return self.rhofunc(Vector2d(self.x[i], self.y[i]))
Beispiel #18
0
    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