Skip to content

Commit 765e9c8

Browse files
authored
Update rxd.MultipleGeometry to support 3D and hybrid models. (#3389)
* Update rxd.MultipleGeometry to support 3D and hybrid models. * Made corrections suggested by @ramcdougal.
1 parent b44291b commit 765e9c8

File tree

2 files changed

+79
-0
lines changed

2 files changed

+79
-0
lines changed

share/lib/python/neuron/rxd/geometry.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import numpy
44
from neuron import h, nrn
55
from .rxdException import RxDException
6+
from itertools import groupby
67

78
try:
89
from neuron.rxd import geometry3d
@@ -151,6 +152,8 @@ def scale_by_constant(scale, f):
151152
inside = RxDGeometry()
152153
if has_geometry3d:
153154
inside.volumes3d = geometry3d.voxelize2
155+
inside._scale_internal_volumes3d = 1
156+
inside._scale_surface_volumes3d = 1
154157
# neighbor_area_fraction can be a constant or a function
155158
inside.neighbor_area_fraction = 1
156159
inside.volumes1d = _volumes1d
@@ -201,6 +204,8 @@ class DistributedBoundary(RxDGeometry):
201204

202205
def __init__(self, area_per_vol, perim_per_area=0):
203206
self._area_per_vol = area_per_vol
207+
self._scale_internal_volumes3d = area_per_vol
208+
self._scale_surface_volumes3d = area_per_vol
204209
self._perim_per_area = 0
205210

206211
self.surface_areas1d = _always_0
@@ -268,6 +273,9 @@ def __init__(
268273
self.is_volume = _always_true
269274
self.is_area = _always_false
270275
self._volume_fraction = volume_fraction
276+
self._scale_internal_volumes3d = volume_fraction
277+
self._scale_surface_volumes3d = volume_fraction
278+
271279
self._surface_fraction = surface_fraction
272280
self._neighbor_areas_fraction = neighbor_areas_fraction
273281
# TODO: does the else case ever make sense?
@@ -585,6 +593,36 @@ def _get_geo(self, sec):
585593
)
586594
return geo
587595

596+
def volumes3d(self, source, dx=0.25):
597+
geos = {}
598+
for sec in source:
599+
geo = self._get_geo(sec)
600+
geos.setdefault(geo, []).append(sec)
601+
internal_voxels, surface_voxels, mesh_grid = geometry3d.voxelize2(source, dx)
602+
internal_key = lambda itm: itm[1][1].sec
603+
surface_key = lambda itm: itm[1][2].sec
604+
internal_by_sec = groupby(internal_voxels.items(), key=internal_key)
605+
surface_by_sec = groupby(surface_voxels.items(), key=surface_key)
606+
for geo, secs in geos.items():
607+
# only scale if not multiplying by 1
608+
if geo._scale_internal_volumes3d != 1:
609+
for sec, voxels in internal_by_sec:
610+
# only scale if sec is in this geometry
611+
if sec in secs:
612+
# scale the internal volume
613+
for key, _ in voxels:
614+
internal_voxels[key][0] *= geo._scale_internal_volumes3d
615+
# only scale if not multiplying by 1
616+
if geo._scale_surface_volumes3d != 1:
617+
for sec, voxels in surface_by_sec:
618+
# only scale if sec is in this geometry
619+
if sec in secs:
620+
# scale the surface volume
621+
for key, _ in voxels:
622+
surface_voxels[key][0] *= geo._scale_surface_volumes3d
623+
624+
return internal_voxels, surface_voxels, mesh_grid
625+
588626
def volumes1d(self, sec):
589627
return self._get_geo(sec).volumes1d(sec)
590628

test/rxd/3d/test_multigeometry.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import pytest
2+
import numpy
3+
from testutils import compare_data, tol
4+
5+
6+
@pytest.mark.parametrize("hybrid", [True, False])
7+
def test_multi_geometry3d(neuron_nosave_instance, hybrid):
8+
"""A model using multiple different geometries in a 3D model"""
9+
10+
h, rxd, save_path = neuron_nosave_instance
11+
dendA = h.Section("dendA")
12+
dendA.diam = 1
13+
dendA.nseg = 3
14+
dendA.L = 5
15+
dendB = h.Section("dendB")
16+
dendB.diam = 1
17+
dendB.nseg = 3
18+
dendB.L = 5
19+
20+
if hybrid:
21+
rxd.set_solve_type([dendA], dimension=3)
22+
else:
23+
rxd.set_solve_type(dimension=3)
24+
25+
geoA = rxd.inside
26+
geoB = rxd.FractionalVolume(volume_fraction=0.5, surface_fraction=1)
27+
geometry = rxd.MultipleGeometry(secs=[dendA, dendB], geos=[geoA, geoB])
28+
r = rxd.Region(h.allsec(), dx=0.75, geometry=geometry)
29+
ca = rxd.Species(r, name="ca", charge=2)
30+
31+
if hybrid:
32+
assert len(ca.nodes(dendA)) == 28
33+
assert (len(ca.nodes(dendB))) == 3
34+
assert sum(ca.nodes(dendA).volume) == 2.671875
35+
assert sum(ca.nodes(dendB).volume) == 1.9634954084936205
36+
37+
else:
38+
assert len(ca.nodes(dendA)) == 28
39+
assert len(ca.nodes(dendA)) == 28
40+
assert sum(ca.nodes(dendA).volume) == 2.671875
41+
assert sum(ca.nodes(dendB).volume) == 1.3359375

0 commit comments

Comments
 (0)