Beispiel #1
0
def det(m):
    hm = h.Matrix(m.shape[0], m.shape[1])
    for i in range(int(hm.nrow())):
        hm.setcol(i, h.Vector(m[:, i]))
    e = h.ref(0)
    d = hm.det(e)
    return d * 10.0**e[0]
Beispiel #2
0
def expm(m):
    hm = h.Matrix(m.shape[0], m.shape[1])
    for i in range(int(hm.nrow())):
        hm.setcol(i, h.Vector(m[:, i]))
    hm = hm.exp()
    mo = numpy.matrix(m)
    print m

    for i in range(int(hm.nrow())):
        for j in range(int(hm.nrow())):
            mo[i, j] = hm.getval(i, j)
    return mo
Beispiel #3
0
def run_single_volts(param_set, stim_data, ntimestep = 10000, dt = 0.02):
    run_file = '../../neuron_genetic_alg/neuron_files/bbp/run_model_cori.hoc'
    h.load_file(run_file)
    total_params_num = len(param_set)
    timestamps = np.array([dt for i in range(ntimestep)])
    h.curr_stim = h.Vector().from_python(stim_data)
    h.transvec = h.Vector(total_params_num, 1).from_python(param_set)
    h.stimtime = h.Matrix(1, len(timestamps)).from_vector(h.Vector().from_python(timestamps))
    h.ntimestep = ntimestep
    h.runStim()
    out = h.vecOut.to_python()
    return np.array(out),np.cumsum(timestamps)
Beispiel #4
0
def neuron_run_model(param_set, stim_name_list):
    h.load_file(run_file)
    volts_list = []
    for elem in stim_name_list:
        curr_stim = h5py.File(stims_path, 'r')[elem][:]
        total_params_num = len(param_set)
        timestamps = np.array([dt for i in range(ntimestep)])
        h.curr_stim = h.Vector().from_python(curr_stim)
        h.transvec = h.Vector(total_params_num, 1).from_python(param_set)
        h.stimtime = h.Matrix(1, len(timestamps)).from_vector(h.Vector().from_python(timestamps))
        h.ntimestep = ntimestep
        h.runStim()
        out = h.vecOut.to_python()
        volts_list.append(out)
    return np.array(volts_list)
Beispiel #5
0
    def _get_nrn_voltages(self):
        """The extracellular data (n_contacts x n_samples)."""
        if len(self._nrn_voltages) > 0:
            assert (self._nrn_voltages.size() == self.n_contacts *
                    self._nrn_n_samples)

            # first reshape to a Neuron Matrix object
            extmat = h.Matrix(self.n_contacts, self._nrn_n_samples)
            extmat.from_vector(self._nrn_voltages)

            # then unpack into 2D python list and return
            return [
                extmat.getrow(ii).to_python() for ii in range(extmat.nrow())
            ]
        else:
            raise RuntimeError('Simulation not yet run!')
Beispiel #6
0
def paranormal0(pt):
    #print ("paranormal0 ", pt)
    # return the normal direction from the inner paraboloid that contains pt
    assert (pt[1] == 0.0)
    # two dimensional problem due to assertion.
    x = pt[0]
    z = pt[2]
    lam = 0.0
    # three equations in three unknowns (x, z) on parboloid (x', z') is pt
    # unknowns x, z, lam
    # a*x^2 - c*z = 0
    # x + lam*2*a*x - x' = 0
    # z - lam*c  - z' = 0
    # can reduce to single cubic equation in x but might as well solve
    # by newton method. Jacobian is
    # [2*a*x      , -c, 0    ]
    # [1 + 2*a*lam, 0 , 2*a*x]
    # [0          , 1 , -c   ]
    b = h.Vector(3)
    m = h.Matrix(3, 3)
    dx = h.Vector(3)
    iter = 0
    for i in range(100):
        mset(m, x, z, lam)
        bset(b, x, z, lam, pt[0], pt[2])
        m.solv(b, dx)
        #dx.printf()
        x += dx.x[0]
        z += dx.x[1]
        lam += dx.x[2]
        iter += 1
        if dx.sumsq() < 1e-14:
            break
    assert (iter < 100)
    global maxiter
    maxiter = iter if iter > maxiter else maxiter
    return normgrad((x, 0, z))
Beispiel #7
0
from neuron import h
m = h.Matrix(3, 4, 2)
m.setval(1, 2, 100)
m.setval(2, 2, 200)


def sparse_print(m):
    j = h.ref(0)
    for i in range(int(m.nrow())):
        print(i, end=' ')
        for jx in range(int(m.sprowlen(i))):
            x = m.spgetrowval(i, jx, j)
            print("  %d:%g" % (j[0], x))
        print()


sparse_print(m)
Beispiel #8
0
    def _build(self, cvode=None, include_celltypes='all'):
        """Assemble NEURON objects for calculating extracellular potentials.

        The handler is set up to maintain a vector of membrane currents at at
        every inner segment of every section of every cell on each CVODE
        integration step. In addition, it records a time vector of sample
        times.

        Parameters
        ----------
        cvode : instance of h.CVode
            Multi order variable time step integration method.
        include_celltypes : str
            String to match against the cell type of each section. Defaults to
            ``'all'``: calculate extracellular potential generated by all
            cells. To restrict this to include only pyramidal cells, use
            ``'Pyr'``. For basket cells, use ``'Basket'``. NB This argument is
            currently not exposed in the API.
        """
        secs_on_rank = h.allsec()  # get all h.Sections known to this MPI rank
        _validate_type(include_celltypes, str)
        _check_option('include_celltypes', include_celltypes,
                      ['all', 'Pyr', 'Basket'])
        if include_celltypes.lower() != 'all':
            secs_on_rank = [
                s for s in secs_on_rank if include_celltypes in s.name()
            ]

        segment_counts = [sec.nseg for sec in secs_on_rank]
        n_total_segments = np.sum(segment_counts)

        # pointers assigned to _ref_i_membrane_ at each EACH internal segment
        self._nrn_imem_ptrvec = h.PtrVector(n_total_segments)
        # placeholder into which pointer values are read on each sim time step
        self._nrn_imem_vec = h.Vector(n_total_segments)

        ptr_idx = 0
        for sec in secs_on_rank:
            for seg in sec:  # section end points (0, 1) not included
                # set Nth pointer to the net membrane current at this segment
                self._nrn_imem_ptrvec.pset(ptr_idx,
                                           sec(seg.x)._ref_i_membrane_)
                ptr_idx += 1
        if ptr_idx != n_total_segments:
            raise RuntimeError(f'Expected {n_total_segments} imem pointers, '
                               f'got {ptr_idx}.')

        # transfer resistances for each segment (keep in Neuron Matrix object)
        self._nrn_r_transfer = h.Matrix(self.n_contacts, n_total_segments)

        for row, pos in enumerate(self.array.positions):
            if self.array.method is not None:
                transfer_resistance = list()
                for sec in secs_on_rank:
                    this_xfer_r = _transfer_resistance(
                        sec,
                        pos,
                        conductivity=self.array.conductivity,
                        method=self.array.method,
                        min_distance=self.array.min_distance)
                    transfer_resistance.extend(this_xfer_r)

                self._nrn_r_transfer.setrow(row, h.Vector(transfer_resistance))
            else:
                # for testing, make a matrix of ones
                self._nrn_r_transfer.setrow(row,
                                            h.Vector(n_total_segments, 1.))

        # record time for each array
        self._nrn_times = h.Vector().record(h._ref_t)

        # contributions of all segments on this rank to total calculated
        # potential at electrode (_PC.allreduce called in _simulate_dipole)
        # NB voltages of all contacts are initialised to 0 mV, i.e., the
        # potential at time 0.0 ms is defined to be zero.
        self._nrn_voltages = h.Vector(self.n_contacts, 0.)

        # NB we must make a copy of the function reference, and keep it for
        # later decoupling using extra_scatter_gather_remove
        # (instead of a new function reference)
        self._recording_callback = self._gather_nrn_voltages
        # Nb extra_scatter_gather is called _after_ the solver takes a step,
        # so the initial state is not recorded (initialised to zero above)
        cvode.extra_scatter_gather(0, self._recording_callback)
Beispiel #9
0
from neuron import h
from math import cos, sin, fabs

h.load_file('nrngui.hoc')

cmat = h.Matrix(2,2,2).ident()

gmat = h.Matrix(2,2,2)
gmat.setval(0,1, -1)
gmat.setval(1,0,  1) # not needed unless experimenting with z2 below

y = h.Vector(2)
y0 = h.Vector(2)
b = h.Vector(2)

def callback():
  #print 'callback t=', h.t
  z1 = y.x[0]
  z2 = -fabs(cos(z1)) # jacobian element is db[1]/dy[0]
  z2 = 0
  gmat.setval(1,0, z2)
  b.x[1] = -sin(z1) + z1*z2

nlm = h.LinearMechanism(callback, cmat, gmat, y, y0, b)


dummy = h.Section()
trajec = h.Vector()
tvec = h.Vector()
trajec.record(y._ref_x[0])
tvec.record(h._ref_t)
Beispiel #10
0
def constructive_neuronal_geometry(source, n_soma_step, dx):
    objects = []

    source_is_import3d = False
    # TODO: come up with a better way of checking type
    if hasattr(source, 'sections'):
        source_is_import3d = True
        cell = source
        # probably an Import3D type
        num_contours = sum(sec.iscontour_ for sec in cell.sections)
        if num_contours > 1:
            raise Exception('more than one contour is not currently supported')
        if num_contours == 1:
            # setup the soma
            # CTNG:soma
            branches = []
            parent_sec_name = []
            for sec in cell.sections:
                if sec.iscontour_:
                    soma_sec = sec.hname()
                    x, y, z = [
                        sec.raw.getrow(i).to_python() for i in xrange(3)
                    ]

                    # compute the center of the contour based on uniformly spaced points around the perimeter
                    center_vec = sec.contourcenter(sec.raw.getrow(0),
                                                   sec.raw.getrow(1),
                                                   sec.raw.getrow(2))
                    x0, y0, z0 = [center_vec.x[i] for i in xrange(3)]
                    somax, somay, somaz = x0, y0, z0

                    xshifted = [xx - x0 for xx in x]
                    yshifted = [yy - y0 for yy in y]
                    # this is a hack to pretend everything is on the same z level
                    zshifted = [0] * len(x)

                    # locate the major and minor axis, adapted from import3d_gui.hoc
                    m = h.Matrix(3, 3)
                    for i, p in enumerate([xshifted, yshifted, zshifted]):
                        for j, q in enumerate([xshifted, yshifted, zshifted]):
                            if j < i: continue
                            v = numpy.dot(p, q)
                            m.setval(i, j, v)
                            m.setval(j, i, v)
                    # CTNG:majoraxis
                    tobj = m.symmeig(m)
                    # major axis is the one with largest eigenvalue
                    major = m.getcol(tobj.max_ind())
                    # minor is normal and in xy plane
                    minor = m.getcol(3 - tobj.min_ind() - tobj.max_ind())
                    #minor.x[2] = 0
                    minor.div(minor.mag())

                    x1 = x0
                    y1 = y0
                    x2 = x1 + major.x[0]
                    y2 = y1 + major.x[1]

                    xs_loop = x + [x[0]]
                    ys_loop = y + [y[0]]

                    # locate the extrema of the major axis CTNG:somaextrema
                    # this is defined by the furthest points on it that lie on the minor axis
                    pts = []
                    pts_sources = {}
                    for x3, y3 in zip(x, y):
                        x4, y4 = x3 + minor.x[0], y3 + minor.x[1]
                        pt = seg_line_intersection(x1,
                                                   y1,
                                                   x2,
                                                   y2,
                                                   x3,
                                                   y3,
                                                   x4,
                                                   y4,
                                                   clip=False)
                        if pt is not None:
                            pts.append(pt)
                            if pt not in pts_sources:
                                pts_sources[pt] = []
                            pts_sources[pt].append((x3, y3))

                    major_p1, major_p2 = extreme_pts(pts)

                    extreme1 = pts_sources[major_p1]
                    extreme2 = pts_sources[major_p2]

                    major_p1, major_p2 = numpy.array(major_p1), numpy.array(
                        major_p2)
                    del pts_sources

                    if len(extreme1) != 1 or len(extreme2) != 1:
                        raise Exception('multiple most extreme points')
                    extreme1 = extreme1[0]
                    extreme2 = extreme2[0]
                    major_length = linalg.norm(major_p1 - major_p2)
                    delta_x, delta_y = major_p2 - major_p1
                    delta_x /= n_soma_step
                    delta_y /= n_soma_step

                    f_pts = [major_p1]
                    f_diams = [0]

                    # CTNG:slicesoma
                    for i in xrange(1, n_soma_step):
                        x0, y0 = major_p1[0] + i * delta_x, major_p1[
                            1] + i * delta_y
                        # slice in dir of minor axis
                        x1, y1 = x0 + minor.x[0], y0 + minor.x[1]
                        pts = []
                        for i in xrange(len(x)):
                            pt = seg_line_intersection(xs_loop[i],
                                                       ys_loop[i],
                                                       xs_loop[i + 1],
                                                       ys_loop[i + 1],
                                                       x0,
                                                       y0,
                                                       x1,
                                                       y1,
                                                       clip=True)
                            if pt is not None: pts.append(pt)
                        p1, p2 = extreme_pts(pts)
                        p1, p2 = numpy.array(p1), numpy.array(p2)
                        cx, cy = (p1 + p2) / 2.
                        f_pts.append((cx, cy))
                        f_diams.append(linalg.norm(p1 - p2))

                    f_pts.append(major_p2)
                    f_diams.append(0)

                    for i in xrange(len(f_pts) - 1):
                        pt1x, pt1y = f_pts[i]
                        pt2x, pt2y = f_pts[i + 1]
                        diam1 = f_diams[i]
                        diam2 = f_diams[i + 1]
                        objects.append(
                            SkewCone(pt1x, pt1y, z0, diam1 * 0.5,
                                     pt1x + delta_x, pt1y + delta_y, z0,
                                     diam2 * 0.5, pt2x, pt2y, z0))
                else:
                    parent_sec_name.append(sec.parentsec.hname())
                    branches.append(sec)
    else:
        h.define_shape()
        soma_sec = None
        branches = []
        for sec in source:
            branches.append(sec)
        # this is ignored in this case, but needs to be same length
        # so this way no extra memory except the pointer
        parent_sec_name = branches

    #####################################################################
    #
    # add the branches
    #
    #####################################################################
    diam_corrections = {None: None}

    while diam_corrections:
        all_cones = []
        pts_cones_db = {}
        diam_db = {}
        for branch, psec in zip(branches, parent_sec_name):
            if source_is_import3d:
                x, y, z = [branch.raw.getrow(i).to_python() for i in xrange(3)]
                d = branch.d.to_python()
            else:
                x = [
                    h.x3d(i, sec=branch)
                    for i in xrange(int(h.n3d(sec=branch)))
                ]
                y = [
                    h.y3d(i, sec=branch)
                    for i in xrange(int(h.n3d(sec=branch)))
                ]
                z = [
                    h.z3d(i, sec=branch)
                    for i in xrange(int(h.n3d(sec=branch)))
                ]
                d = [
                    h.diam3d(i, sec=branch)
                    for i in xrange(int(h.n3d(sec=branch)))
                ]

            # make sure that all the ones that connect to the soma do in fact connect
            # do this by connecting to local center axis
            # CTNG:connectdends

            if psec == soma_sec:
                pt = (x[1], y[1], z[1])
                cp = closest_pt(pt, f_pts, somaz)
                # NEURON includes the wire point at the center; we want to connect
                # to the closest place on the soma's axis instead with full diameter
                x, y, z, d = [cp[0]] + [X for X in x[1:]], [cp[1]] + [
                    Y for Y in y[1:]
                ], [somaz] + [Z for Z in z[1:]], [d[1]] + [D for D in d[1:]]

            for i in xrange(len(x) - 1):
                d0, d1 = d[i:i + 2]
                if (x[i] != x[i + 1] or y[i] != y[i + 1] or z[i] != z[i + 1]):
                    # short section check
                    #if linalg.norm((x[i + 1] - x[i], y[i + 1] - y[i], z[i + 1] - z[i])) < (d1 + d0) * 0.5:
                    #    short_segs += 1
                    axisx, axisy, axisz, deltad = x[i + 1] - x[i], y[
                        i + 1] - y[i], z[i + 1] - z[i], d1 - d0
                    axislength = (axisx**2 + axisy**2 + axisz**2)**0.5
                    axisx /= axislength
                    axisy /= axislength
                    axisz /= axislength
                    deltad /= axislength
                    x0, y0, z0 = x[i], y[i], z[i]
                    x1, y1, z1 = x[i + 1], y[i + 1], z[i + 1]
                    if (x0, y0, z0) in diam_corrections:
                        d0 = diam_corrections[(x0, y0, z0)]
                    if (x1, y1, z1) in diam_corrections:
                        d1 = diam_corrections[(x1, y1, z1)]

                    if d0 != d1:
                        all_cones.append(
                            Cone(x0, y0, z0, d0 * 0.5, x1, y1, z1, d1 * 0.5))
                    else:
                        all_cones.append(
                            Cylinder(x0, y0, z0, x1, y1, z1, d1 * 0.5))

                    register(pts_cones_db, (x0, y0, z0), all_cones[-1])
                    register(pts_cones_db, (x1, y1, z1), all_cones[-1])
                    register(diam_db, (x0, y0, z0), d0)
                    register(diam_db, (x1, y1, z1), d1)

        # at join, should always be the size of the biggest branch
        # this is different behavior than NEURON, which continues the size of the
        # first point away from the join to the join
        diam_corrections = {}
        for pt in diam_db:
            vals = diam_db[pt]
            if max(vals) != min(vals):
                diam_corrections[pt] = max(vals)

    cone_clip_db = {cone: [] for cone in all_cones}

    join_counts = {
        '2m': 0,
        '2s': 0,
        '3m': 0,
        '3s': 0,
        '4m': 0,
        '4s': 0,
        '0m': 0,
        '0s': 0,
        '1m': 0,
        '1s': 0
    }
    for cone in all_cones:
        x1, y1, z1, r1 = cone._x0, cone._y0, cone._z0, cone._r0
        x2, y2, z2, r2 = cone._x1, cone._y1, cone._z1, cone._r1
        pt1 = numpy.array([x1, y1, z1])
        pt2 = numpy.array([x2, y2, z2])
        axis = (pt2 - pt1) / linalg.norm(pt2 - pt1)
        left_neighbors = list(pts_cones_db[(x1, y1, z1)])
        right_neighbors = list(pts_cones_db[(x2, y2, z2)])
        left_neighbors.remove(cone)
        right_neighbors.remove(cone)
        if not left_neighbors: left_neighbors = [None]
        if not right_neighbors: right_neighbors = [None]
        for neighbor_left, neighbor_right in itertools.product(
                left_neighbors, right_neighbors):
            clips = []
            # process the join on the "left" (end 1)
            if neighbor_left is not None:
                # any joins are created on the left pass; the right pass will only do clippings
                x0, y0, z0, r0 = neighbor_left._x0, neighbor_left._y0, neighbor_left._z0, neighbor_left._r0
                if x0 == x1 and y0 == y1 and z0 == z1:
                    x0, y0, z0, r0 = neighbor_left._x1, neighbor_left._y1, neighbor_left._z1, neighbor_left._r1
                pt0 = numpy.array([x0, y0, z0])
                naxis = (pt1 - pt0) / linalg.norm(pt1 - pt0)
                # no need to clip if the cones are perfectly aligned
                if any(axis != naxis):
                    if r0 == r1 == r2:
                        # simplest join: two cylinders (no need for all that nastiness below)
                        sp = Sphere(x1, y1, z1, r1)
                        sp.set_clip([
                            Plane(x0, y0, z0, -naxis[0], -naxis[1], -naxis[2]),
                            Plane(x2, y2, z2, axis[0], axis[1], axis[2])
                        ])
                        objects.append(sp)
                    else:
                        # is the turn sharp or not
                        # CTNG:joinangle
                        sharp_turn = numpy.dot(axis, naxis) < 0
                        # locate key vectors
                        plane_normal = numpy.cross(axis, naxis)
                        radial_vec = numpy.cross(plane_normal, axis)
                        nradial_vec = numpy.cross(plane_normal, naxis)
                        # normalize all of these
                        radial_vec /= linalg.norm(radial_vec)
                        nradial_vec /= linalg.norm(nradial_vec)

                        # count the corners that are inside the other cone (for both ways)
                        # CTNG:outsidecorners
                        my_corner_count = count_outside(
                            neighbor_left,
                            [pt1 + r1 * radial_vec, pt1 - r1 * radial_vec])
                        corner_count = my_corner_count + count_outside(
                            cone,
                            [pt1 + r1 * nradial_vec, pt1 - r1 * nradial_vec])

                        # if corner_count == 0, then probably all nan's from size 0 meeting size 0; ignore
                        # if is 1, probably parallel; no joins
                        #                if corner_count not in (1, 2, 3, 4):
                        #                    print 'corner_count: ', corner_count, [pt1 + r1 * radial_vec, pt1 - r1 * radial_vec] + [pt1 + r1 * nradial_vec, pt1 - r1 * nradial_vec]
                        if corner_count == 2:
                            # CTNG:2outside
                            # add clipped sphere; same rule if sharp or mild turn
                            objects += join_outside(x0, y0, z0, r0, x1, y1, z1,
                                                    r1, x2, y2, z2, r2, dx)
                        elif corner_count == 3:
                            sp = Sphere(x1, y1, z1, r1)
                            if sharp_turn:
                                # CTNG:3outobtuse
                                if my_corner_count == 1:
                                    sp.set_clip([
                                        Plane(x1, y1, z1, -naxis[0], -naxis[1],
                                              -naxis[2])
                                    ])
                                else:
                                    sp.set_clip([
                                        Plane(x1, y1, z1, axis[0], axis[1],
                                              axis[2])
                                    ])
                                objects.append(sp)
                            else:
                                # CTNG:3outacute
                                objects += join_outside(
                                    x0, y0, z0, r0, x1, y1, z1, r1, x2, y2, z2,
                                    r2, dx)
                                if my_corner_count == 1:
                                    objects.append(
                                        tangent_sphere(neighbor_left, 1))
                                    objects[-1].set_clip([
                                        Plane(x2, y2, z2, naxis[0], naxis[1],
                                              naxis[2])
                                    ])
                                else:
                                    objects.append(tangent_sphere(cone, 0))
                                    objects[-1].set_clip([
                                        Plane(x0, y0, z0, -axis[0], -axis[1],
                                              -axis[2])
                                    ])

                        elif corner_count == 4:
                            sp = Sphere(x1, y1, z1, r1)
                            if sharp_turn:
                                # CTNG:4outobtuse
                                # join with the portions of a sphere that are outside at least one of the planes
                                sp.set_clip([
                                    Union([
                                        Plane(x1, y1, z1, axis[0], axis[1],
                                              axis[2]),
                                        Plane(x1, y1, z1, -naxis[0], -naxis[1],
                                              -naxis[2])
                                    ])
                                ])
                                objects.append(sp)
                            else:
                                # CTNG:4outacute (+ 1 more)
                                # join with the portions of a sphere that are outside both planes
                                objects += join_outside(
                                    x0, y0, z0, r0, x1, y1, z1, r1, x2, y2, z2,
                                    r2, dx)
                                # AND clip the cone to not extend pass the union of the neighbor's plane and the neighbor
                                if r0 == r1:
                                    neighbor_copy = Cylinder(
                                        x0, y0, z0, x1, y1, z1, r0)
                                else:
                                    neighbor_copy = Cone(
                                        x0, y0, z0, r0, x1, y1, z1, r1)
                                clips.append(
                                    Union([
                                        Plane(x1, y1, z1, -naxis[0], -naxis[1],
                                              -naxis[2]), neighbor_copy
                                    ]))

                        join_type = '%d%s' % (corner_count,
                                              's' if sharp_turn else 'm')
                        join_counts[join_type] += 1

            if neighbor_right is not None:
                # any joins are created on the left pass; the right pass will only do clippings
                x3, y3, z3, r3 = neighbor_right._x0, neighbor_right._y0, neighbor_right._z0, neighbor_right._r0
                if x2 == x3 and y2 == y3 and z2 == z3:
                    x3, y3, z3, r3 = neighbor_right._x1, neighbor_right._y1, neighbor_right._z1, neighbor_right._r1
                pt3 = numpy.array([x3, y3, z3])
                naxis = (pt3 - pt2) / linalg.norm(pt3 - pt2)

                # no need to clip if the cones are perfectly aligned
                if any(axis != naxis):
                    # locate key vectors
                    plane_normal = numpy.cross(axis, naxis)
                    radial_vec = numpy.cross(plane_normal, axis)
                    radial_vec_norm = linalg.norm(radial_vec)

                    # we check again because sometimes there are roundoff errors that this catches
                    if radial_vec_norm:

                        # is the turn sharp or not
                        sharp_turn = numpy.dot(axis, naxis) < 0

                        nradial_vec = numpy.cross(plane_normal, naxis)
                        # normalize all of these
                        radial_vec /= radial_vec_norm
                        nradial_vec /= linalg.norm(nradial_vec)
                        # count the corners that are inside the other cone (for both ways)
                        my_corner_count = count_outside(
                            neighbor_right,
                            [pt2 + r2 * radial_vec, pt2 - r2 * radial_vec])
                        corner_count = my_corner_count + count_outside(
                            cone,
                            [pt2 + r2 * nradial_vec, pt2 - r2 * nradial_vec])
                        if corner_count == 2:
                            # no clipping; already joined
                            pass
                        elif corner_count == 3:
                            pass

                        elif corner_count == 4:
                            # CTNG:4outacute (+ 1 more)
                            # already joined; just clip (only in mild turn case)
                            if not sharp_turn:
                                if r2 == r3:
                                    neighbor_copy = Cylinder(
                                        x2, y2, z2, x3, y3, z3, r3)
                                else:
                                    neighbor_copy = Cone(
                                        x2, y2, z2, r2, x3, y3, z3, r3)
                                #print 'cc=4: (%g, %g, %g; %g) (%g, %g, %g; %g) (%g, %g, %g; %g) ' % (x1, y1, z1, r1, x2, y2, z2, r2, x3, y3, z3, r3)
                                clips.append(
                                    Union([
                                        Plane(x2, y2, z2, naxis[0], naxis[1],
                                              naxis[2]), neighbor_copy
                                    ]))

            if clips:
                cone_clip_db[cone].append(Intersection(clips))

    #print 'join_counts:'
    #print join_counts

    for cone in all_cones:
        clip = cone_clip_db[cone]
        if clip:
            cone.set_clip([Union(clip)])

    #####################################################################
    #
    # add the clipped objects to the list
    #
    #####################################################################

    objects += all_cones
    return objects