openpnm 1.0.0__zip
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- OpenPNM-1.1/MANIFEST.in +2 -0
- OpenPNM-1.1/OpenPNM/Algorithms/__FickianDiffusion__.py +67 -0
- OpenPNM-1.1/OpenPNM/Algorithms/__FourierConduction__.py +63 -0
- OpenPNM-1.1/OpenPNM/Algorithms/__GenericAlgorithm__.py +235 -0
- OpenPNM-1.1/OpenPNM/Algorithms/__GenericLinearTransport__.py +641 -0
- OpenPNM-1.1/OpenPNM/Algorithms/__InvasionPercolationForImbibition__.py +703 -0
- OpenPNM-1.1/OpenPNM/Algorithms/__InvasionPercolationTimed__.py +702 -0
- OpenPNM-1.1/OpenPNM/Algorithms/__InvasionPercolation__.py +156 -0
- OpenPNM-1.1/OpenPNM/Algorithms/__OhmicConduction__.py +64 -0
- OpenPNM-1.1/OpenPNM/Algorithms/__OrdinaryPercolation__.py +402 -0
- OpenPNM-1.1/OpenPNM/Algorithms/__StokesFlow__.py +64 -0
- OpenPNM-1.1/OpenPNM/Algorithms/__Tortuosity__.py +91 -0
- OpenPNM-1.1/OpenPNM/Algorithms/__init__.py +48 -0
- OpenPNM-1.1/OpenPNM/Base/__Controller__.py +480 -0
- OpenPNM-1.1/OpenPNM/Base/__Core__.py +1522 -0
- OpenPNM-1.1/OpenPNM/Base/__ModelsDict__.py +345 -0
- OpenPNM-1.1/OpenPNM/Base/__Tools__.py +72 -0
- OpenPNM-1.1/OpenPNM/Base/__init__.py +32 -0
- OpenPNM-1.1/OpenPNM/Geometry/__Boundary__.py +80 -0
- OpenPNM-1.1/OpenPNM/Geometry/__Cube_and_Cuboid__.py +64 -0
- OpenPNM-1.1/OpenPNM/Geometry/__GenericGeometry__.py +106 -0
- OpenPNM-1.1/OpenPNM/Geometry/__SGL10__.py +67 -0
- OpenPNM-1.1/OpenPNM/Geometry/__Stick_and_Ball__.py +68 -0
- OpenPNM-1.1/OpenPNM/Geometry/__TestGeometry__.py +51 -0
- OpenPNM-1.1/OpenPNM/Geometry/__Toray090__.py +68 -0
- OpenPNM-1.1/OpenPNM/Geometry/__Voronoi__.py +98 -0
- OpenPNM-1.1/OpenPNM/Geometry/__init__.py +47 -0
- OpenPNM-1.1/OpenPNM/Geometry/models/__init__.py +33 -0
- OpenPNM-1.1/OpenPNM/Geometry/models/pore_area.py +27 -0
- OpenPNM-1.1/OpenPNM/Geometry/models/pore_centroid.py +35 -0
- OpenPNM-1.1/OpenPNM/Geometry/models/pore_diameter.py +127 -0
- OpenPNM-1.1/OpenPNM/Geometry/models/pore_misc.py +55 -0
- OpenPNM-1.1/OpenPNM/Geometry/models/pore_seed.py +212 -0
- OpenPNM-1.1/OpenPNM/Geometry/models/pore_surface_area.py +28 -0
- OpenPNM-1.1/OpenPNM/Geometry/models/pore_vertices.py +19 -0
- OpenPNM-1.1/OpenPNM/Geometry/models/pore_volume.py +133 -0
- OpenPNM-1.1/OpenPNM/Geometry/models/throat_area.py +47 -0
- OpenPNM-1.1/OpenPNM/Geometry/models/throat_centroid.py +80 -0
- OpenPNM-1.1/OpenPNM/Geometry/models/throat_diameter.py +106 -0
- OpenPNM-1.1/OpenPNM/Geometry/models/throat_length.py +95 -0
- OpenPNM-1.1/OpenPNM/Geometry/models/throat_misc.py +42 -0
- OpenPNM-1.1/OpenPNM/Geometry/models/throat_normal.py +31 -0
- OpenPNM-1.1/OpenPNM/Geometry/models/throat_offset_vertices.py +191 -0
- OpenPNM-1.1/OpenPNM/Geometry/models/throat_perimeter.py +26 -0
- OpenPNM-1.1/OpenPNM/Geometry/models/throat_seed.py +12 -0
- OpenPNM-1.1/OpenPNM/Geometry/models/throat_shape_factor.py +37 -0
- OpenPNM-1.1/OpenPNM/Geometry/models/throat_surface_area.py +44 -0
- OpenPNM-1.1/OpenPNM/Geometry/models/throat_vector.py +27 -0
- OpenPNM-1.1/OpenPNM/Geometry/models/throat_vertices.py +19 -0
- OpenPNM-1.1/OpenPNM/Geometry/models/throat_volume.py +45 -0
- OpenPNM-1.1/OpenPNM/Network/__Cubic__.py +316 -0
- OpenPNM-1.1/OpenPNM/Network/__DelaunayCubic__.py +127 -0
- OpenPNM-1.1/OpenPNM/Network/__Delaunay__.py +600 -0
- OpenPNM-1.1/OpenPNM/Network/__GenericNetwork__.py +1184 -0
- OpenPNM-1.1/OpenPNM/Network/__MatFile__.py +331 -0
- OpenPNM-1.1/OpenPNM/Network/__TestNet__.py +109 -0
- OpenPNM-1.1/OpenPNM/Network/__init__.py +40 -0
- OpenPNM-1.1/OpenPNM/Network/models/__init__.py +12 -0
- OpenPNM-1.1/OpenPNM/Network/models/pore_topology.py +106 -0
- OpenPNM-1.1/OpenPNM/Phases/__Air__.py +63 -0
- OpenPNM-1.1/OpenPNM/Phases/__GenericPhase__.py +146 -0
- OpenPNM-1.1/OpenPNM/Phases/__Mercury__.py +71 -0
- OpenPNM-1.1/OpenPNM/Phases/__TestPhase__.py +46 -0
- OpenPNM-1.1/OpenPNM/Phases/__Water__.py +56 -0
- OpenPNM-1.1/OpenPNM/Phases/__init__.py +38 -0
- OpenPNM-1.1/OpenPNM/Phases/models/__init__.py +22 -0
- OpenPNM-1.1/OpenPNM/Phases/models/contact_angle.py +34 -0
- OpenPNM-1.1/OpenPNM/Phases/models/density.py +81 -0
- OpenPNM-1.1/OpenPNM/Phases/models/diffusivity.py +95 -0
- OpenPNM-1.1/OpenPNM/Phases/models/electrical_conductivity.py +10 -0
- OpenPNM-1.1/OpenPNM/Phases/models/misc.py +125 -0
- OpenPNM-1.1/OpenPNM/Phases/models/molar_density.py +69 -0
- OpenPNM-1.1/OpenPNM/Phases/models/molar_mass.py +31 -0
- OpenPNM-1.1/OpenPNM/Phases/models/surface_tension.py +104 -0
- OpenPNM-1.1/OpenPNM/Phases/models/thermal_conductivity.py +98 -0
- OpenPNM-1.1/OpenPNM/Phases/models/vapor_pressure.py +69 -0
- OpenPNM-1.1/OpenPNM/Phases/models/viscosity.py +103 -0
- OpenPNM-1.1/OpenPNM/Physics/__GenericPhysics__.py +111 -0
- OpenPNM-1.1/OpenPNM/Physics/__Standard__.py +51 -0
- OpenPNM-1.1/OpenPNM/Physics/__TestPhysics__.py +50 -0
- OpenPNM-1.1/OpenPNM/Physics/__init__.py +30 -0
- OpenPNM-1.1/OpenPNM/Physics/models/__init__.py +18 -0
- OpenPNM-1.1/OpenPNM/Physics/models/capillary_pressure.py +122 -0
- OpenPNM-1.1/OpenPNM/Physics/models/diffusive_conductance.py +82 -0
- OpenPNM-1.1/OpenPNM/Physics/models/electrical_conductance.py +59 -0
- OpenPNM-1.1/OpenPNM/Physics/models/generic_source_term.py +564 -0
- OpenPNM-1.1/OpenPNM/Physics/models/hydraulic_conductance.py +76 -0
- OpenPNM-1.1/OpenPNM/Physics/models/multiphase.py +133 -0
- OpenPNM-1.1/OpenPNM/Physics/models/thermal_conductance.py +67 -0
- OpenPNM-1.1/OpenPNM/Postprocessing/Graphics.py +251 -0
- OpenPNM-1.1/OpenPNM/Postprocessing/Plots.py +369 -0
- OpenPNM-1.1/OpenPNM/Postprocessing/__init__.py +10 -0
- OpenPNM-1.1/OpenPNM/Utilities/IO.py +277 -0
- OpenPNM-1.1/OpenPNM/Utilities/Shortcuts.py +17 -0
- OpenPNM-1.1/OpenPNM/Utilities/__init__.py +16 -0
- OpenPNM-1.1/OpenPNM/Utilities/misc.py +226 -0
- OpenPNM-1.1/OpenPNM/Utilities/transformations.py +1923 -0
- OpenPNM-1.1/OpenPNM/Utilities/vertexops.py +824 -0
- OpenPNM-1.1/OpenPNM/__init__.py +56 -0
- OpenPNM-1.1/OpenPNM.egg-info/PKG-INFO +11 -0
- OpenPNM-1.1/OpenPNM.egg-info/SOURCES.txt +107 -0
- OpenPNM-1.1/OpenPNM.egg-info/dependency_links.txt +1 -0
- OpenPNM-1.1/OpenPNM.egg-info/requires.txt +1 -0
- OpenPNM-1.1/OpenPNM.egg-info/top_level.txt +1 -0
- OpenPNM-1.1/PKG-INFO +11 -0
- OpenPNM-1.1/README.txt +88 -0
- OpenPNM-1.1/setup.cfg +7 -0
- OpenPNM-1.1/setup.py +39 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
r"""
|
|
2
|
+
===============================================================================
|
|
3
|
+
Submodule -- diffusive_conductance
|
|
4
|
+
===============================================================================
|
|
5
|
+
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import scipy as sp
|
|
9
|
+
|
|
10
|
+
def conduit_conductance(physics,
|
|
11
|
+
phase,
|
|
12
|
+
network,
|
|
13
|
+
throat_conductance,
|
|
14
|
+
throat_occupancy='throat.occupancy',
|
|
15
|
+
pore_occupancy='pore.occupancy',
|
|
16
|
+
mode='strict',
|
|
17
|
+
factor=1e-6,
|
|
18
|
+
**kwargs):
|
|
19
|
+
r"""
|
|
20
|
+
Add a new multiphase conductance property to the conduits of network, where a
|
|
21
|
+
conduit is ( 1/2 pore - full throat - 1/2 pore ) based on the areas.
|
|
22
|
+
|
|
23
|
+
This method "closes" conduits that are not sufficiently filled with the
|
|
24
|
+
specified phase by multiplying the original conductance by a very small *factor*.
|
|
25
|
+
|
|
26
|
+
Parameters
|
|
27
|
+
----------
|
|
28
|
+
network : OpenPNM Network Object
|
|
29
|
+
|
|
30
|
+
phase : OpenPNM Phase Object
|
|
31
|
+
The phase of interest
|
|
32
|
+
|
|
33
|
+
occupied_condition : 'occupancy'
|
|
34
|
+
The name of the pore and throat property that dictates whether conduit is "closed" or not
|
|
35
|
+
|
|
36
|
+
mode : 'strict' or 'medium' or 'loose'
|
|
37
|
+
How agressive the method should be in "closing" conduits.
|
|
38
|
+
'strict' implies that if any pore or throat in the conduit is unoccupied by the given phase, the conduit is closed.
|
|
39
|
+
'medium' implies that if either the throat or both pores are unoccupied, the conduit is closed
|
|
40
|
+
'loose' will only close the conduit if the throat is unoccupied.
|
|
41
|
+
|
|
42
|
+
factor : 1/1e3
|
|
43
|
+
The "closing factor" which becomes multiplied to the original conduit's conductance to severely limit transport.
|
|
44
|
+
|
|
45
|
+
Notes
|
|
46
|
+
-----
|
|
47
|
+
This function requires that all the necessary phase properties already be
|
|
48
|
+
calculated.
|
|
49
|
+
|
|
50
|
+
"""
|
|
51
|
+
throats = phase.throats(physics.name)
|
|
52
|
+
if (mode == 'loose'):
|
|
53
|
+
closed_conduits = -sp.array(phase[throat_occupancy],dtype=bool)
|
|
54
|
+
else:
|
|
55
|
+
throats_closed = -sp.array(phase[throat_occupancy],dtype=bool)
|
|
56
|
+
connected_pores = network.find_connected_pores(throats)
|
|
57
|
+
pores_1 = connected_pores[:,0]
|
|
58
|
+
pores_2 = connected_pores[:,1]
|
|
59
|
+
pores_1_closed = -sp.array(phase[pore_occupancy][pores_1],dtype=bool)
|
|
60
|
+
pores_2_closed = -sp.array(phase[pore_occupancy][pores_2],dtype=bool)
|
|
61
|
+
if(mode == 'medium'):
|
|
62
|
+
closed_conduits = throats_closed | (pores_1_closed & pores_2_closed)
|
|
63
|
+
|
|
64
|
+
if(mode == 'strict'):
|
|
65
|
+
closed_conduits = pores_1_closed | throats_closed | pores_2_closed
|
|
66
|
+
open_conduits = -closed_conduits
|
|
67
|
+
throat_value = phase[throat_conductance]
|
|
68
|
+
value = throat_value*open_conduits + throat_value*closed_conduits*factor
|
|
69
|
+
return value
|
|
70
|
+
|
|
71
|
+
def late_throat_filling(network,phase,Pc_star,eta):
|
|
72
|
+
pass
|
|
73
|
+
|
|
74
|
+
def late_pore_filling(physics,
|
|
75
|
+
phase,
|
|
76
|
+
network,
|
|
77
|
+
Pc,
|
|
78
|
+
Swp_star=0.2,
|
|
79
|
+
eta=3,
|
|
80
|
+
wetting_phase=False,
|
|
81
|
+
pore_occupancy='pore.occupancy',
|
|
82
|
+
throat_capillary_pressure='throat.capillary_pressure',
|
|
83
|
+
**kwargs):
|
|
84
|
+
r'''
|
|
85
|
+
Applies a late pore filling model to calculate fractional pore filling as
|
|
86
|
+
a function of applied capillary pressure.
|
|
87
|
+
|
|
88
|
+
Parameters
|
|
89
|
+
----------
|
|
90
|
+
Pc : float
|
|
91
|
+
The capillary pressure in the non-wetting phase (Pc > 0)
|
|
92
|
+
Swp_star : float
|
|
93
|
+
The residual wetting phase in an invaded pore immediately after
|
|
94
|
+
nonwetting phase invasion
|
|
95
|
+
eta : float
|
|
96
|
+
Exponent to control the rate at which wetting phase is displaced
|
|
97
|
+
wetting_phase : boolean
|
|
98
|
+
Indicates whether supplied phase is the wetting or non-wetting phase
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
'''
|
|
102
|
+
pores = phase.pores(physics.name)
|
|
103
|
+
prop = phase[throat_capillary_pressure]
|
|
104
|
+
neighborTs = network.find_neighbor_throats(pores,flatten=False)
|
|
105
|
+
Pc_star = sp.array([sp.amin(prop[row]) for row in neighborTs])
|
|
106
|
+
Swp = Swp_star*(Pc_star/Pc)**eta
|
|
107
|
+
if wetting_phase:
|
|
108
|
+
values = Swp*phase[pore_occupancy]*(Pc_star<Pc)
|
|
109
|
+
else:
|
|
110
|
+
values = (1-Swp)*(1-phase[pore_occupancy])*(Pc_star<Pc)
|
|
111
|
+
return values
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
r"""
|
|
2
|
+
===============================================================================
|
|
3
|
+
Submodule -- thermal_conductance
|
|
4
|
+
===============================================================================
|
|
5
|
+
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import scipy as _sp
|
|
9
|
+
|
|
10
|
+
def series_resistors(physics,
|
|
11
|
+
phase,
|
|
12
|
+
network,
|
|
13
|
+
thermal_conductivity = 'pore.thermal_conductivity',
|
|
14
|
+
pore_diameter = 'pore.diameter',
|
|
15
|
+
pore_area = 'pore.area',
|
|
16
|
+
throat_area = 'throat.area',
|
|
17
|
+
throat_length = 'throat.length',
|
|
18
|
+
**kwargs):
|
|
19
|
+
r"""
|
|
20
|
+
Calculate the thermal conductance of void conduits in network ( 1/2 pore - full throat - 1/2 pore ) based on size (assuming cylindrical geometry)
|
|
21
|
+
|
|
22
|
+
Parameters
|
|
23
|
+
----------
|
|
24
|
+
network : OpenPNM Network Object
|
|
25
|
+
|
|
26
|
+
phase : OpenPNM Phase Object
|
|
27
|
+
The phase of interest
|
|
28
|
+
|
|
29
|
+
Notes
|
|
30
|
+
-----
|
|
31
|
+
(1) This function requires that all the necessary phase properties already
|
|
32
|
+
be calculated.
|
|
33
|
+
|
|
34
|
+
(2) This function calculates the specified property for the *entire*
|
|
35
|
+
network then extracts the values for the appropriate throats at the end.
|
|
36
|
+
|
|
37
|
+
"""
|
|
38
|
+
#Get Nt-by-2 list of pores connected to each throat
|
|
39
|
+
Ps = network['throat.conns']
|
|
40
|
+
#Get properties in every pore in the network
|
|
41
|
+
kp = phase[thermal_conductivity]
|
|
42
|
+
kt = phase.interpolate_data(kp)
|
|
43
|
+
#Find g for half of pore 1
|
|
44
|
+
pdia = network[pore_diameter]
|
|
45
|
+
parea = network[pore_area]
|
|
46
|
+
pdia1 = pdia[Ps[:,0]]
|
|
47
|
+
pdia2 = pdia[Ps[:,1]]
|
|
48
|
+
#remove any non-positive lengths
|
|
49
|
+
pdia1[pdia1<=0] = 1e-12
|
|
50
|
+
pdia2[pdia2<=0] = 1e-12
|
|
51
|
+
gp1 = kt*parea[Ps[:,0]]/(0.5*pdia1)
|
|
52
|
+
gp1[~(gp1>0)] = _sp.inf #Set 0 conductance pores (boundaries) to inf
|
|
53
|
+
#Find g for half of pore 2
|
|
54
|
+
gp2 = kt*parea[Ps[:,1]]/(0.5*pdia2)
|
|
55
|
+
gp2[~(gp2>0)] = _sp.inf #Set 0 conductance pores (boundaries) to inf
|
|
56
|
+
#Find g for full throat
|
|
57
|
+
tarea = network[throat_area]
|
|
58
|
+
tlen = network[throat_length]
|
|
59
|
+
#remove any non-positive lengths
|
|
60
|
+
tlen[tlen<=0] = 1e-12
|
|
61
|
+
gt = kt*tarea/tlen
|
|
62
|
+
value = (1/gt + 1/gp1 + 1/gp2)**(-1)
|
|
63
|
+
value = value[phase.throats(physics.name)]
|
|
64
|
+
return value
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
from tempfile import NamedTemporaryFile
|
|
2
|
+
from subprocess import call
|
|
3
|
+
from itertools import count
|
|
4
|
+
import numpy as np
|
|
5
|
+
from matplotlib import cm
|
|
6
|
+
|
|
7
|
+
try:
|
|
8
|
+
import vtk
|
|
9
|
+
except ImportError:
|
|
10
|
+
vtk = type("", (), {'vtkActor': object})
|
|
11
|
+
|
|
12
|
+
class Actor(vtk.vtkActor):
|
|
13
|
+
|
|
14
|
+
def __init__(self):
|
|
15
|
+
raise NotImplementedError()
|
|
16
|
+
|
|
17
|
+
def pointArray(self, _points):
|
|
18
|
+
points = vtk.vtkPoints()
|
|
19
|
+
for x,y,z in _points:
|
|
20
|
+
points.InsertNextPoint(x, y, z)
|
|
21
|
+
return points
|
|
22
|
+
|
|
23
|
+
def lineArray(self, _lines):
|
|
24
|
+
lines = vtk.vtkCellArray()
|
|
25
|
+
for id_set in _lines:
|
|
26
|
+
l = vtk.vtkIdList()
|
|
27
|
+
for i in id_set:
|
|
28
|
+
l.InsertNextId(i)
|
|
29
|
+
lines.InsertNextCell(l)
|
|
30
|
+
return lines
|
|
31
|
+
|
|
32
|
+
def faceArray(self, faces):
|
|
33
|
+
lines = vtk.vtkCellArray()
|
|
34
|
+
for face in faces:
|
|
35
|
+
l = vtk.vtkIdList()
|
|
36
|
+
for i in face:
|
|
37
|
+
l.InsertNextId(i)
|
|
38
|
+
lines.InsertNextCell(l)
|
|
39
|
+
return lines
|
|
40
|
+
|
|
41
|
+
def floatArray(self, array):
|
|
42
|
+
floats = vtk.vtkFloatArray()
|
|
43
|
+
for n in array:
|
|
44
|
+
floats.InsertNextValue(n)
|
|
45
|
+
return floats
|
|
46
|
+
|
|
47
|
+
def colorArray(self, array, cmap=None):
|
|
48
|
+
colors = vtk.vtkUnsignedCharArray()
|
|
49
|
+
colors.SetNumberOfComponents(3)
|
|
50
|
+
if cmap is None:
|
|
51
|
+
cmap = 'coolwarm'
|
|
52
|
+
colormap = cm.get_cmap(cmap)
|
|
53
|
+
mapped = colormap(array)
|
|
54
|
+
for r,g,b,a in 255*mapped:
|
|
55
|
+
colors.InsertNextTuple3(r,g,b)
|
|
56
|
+
return colors
|
|
57
|
+
|
|
58
|
+
def update(self, t=0):
|
|
59
|
+
pass
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class Wires(Actor):
|
|
63
|
+
|
|
64
|
+
def __init__(self, vertex_coords, edge_pairs, vertex_weights=None, alpha=1, cmap=None):
|
|
65
|
+
self.polydata = vtk.vtkPolyData()
|
|
66
|
+
self.polydata.SetPoints(self.pointArray(vertex_coords))
|
|
67
|
+
self.polydata.SetLines(self.lineArray(edge_pairs))
|
|
68
|
+
self.mapper = vtk.vtkPolyDataMapper()
|
|
69
|
+
self.mapper.SetInput(self.polydata)
|
|
70
|
+
self.SetMapper(self.mapper)
|
|
71
|
+
|
|
72
|
+
if vertex_weights is None:
|
|
73
|
+
weights = np.atleast_2d([128 for _ in vertex_coords])
|
|
74
|
+
else:
|
|
75
|
+
weights = np.atleast_2d(vertex_weights)
|
|
76
|
+
weights = np.subtract(weights, weights.min())
|
|
77
|
+
weights = np.true_divide(weights, weights.max())
|
|
78
|
+
self.weights = weights
|
|
79
|
+
self.cmap = cmap
|
|
80
|
+
self.update()
|
|
81
|
+
|
|
82
|
+
self.GetProperty().SetOpacity(alpha)
|
|
83
|
+
|
|
84
|
+
def update(self, t=0):
|
|
85
|
+
i = t % len(self.weights)
|
|
86
|
+
self.polydata.GetPointData().SetScalars(self.colorArray(self.weights[i], self.cmap))
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class Surface(Actor):
|
|
90
|
+
|
|
91
|
+
def __init__(self, vertex_coords, edge_pairs, vertex_weights=None, alpha=1,
|
|
92
|
+
cmap=None, offset=0):
|
|
93
|
+
self.offset = offset
|
|
94
|
+
self.polydata = vtk.vtkPolyData()
|
|
95
|
+
self.polydata.SetPoints(self.pointArray(vertex_coords))
|
|
96
|
+
self.polydata.SetLines(self.lineArray(edge_pairs))
|
|
97
|
+
self.mapper = vtk.vtkPolyDataMapper()
|
|
98
|
+
self.mapper.SetInput(self.polydata)
|
|
99
|
+
self.SetMapper(self.mapper)
|
|
100
|
+
|
|
101
|
+
if vertex_weights is None:
|
|
102
|
+
weights = np.atleast_2d([128 for _ in vertex_coords])
|
|
103
|
+
else:
|
|
104
|
+
weights = np.atleast_2d(vertex_weights)
|
|
105
|
+
self.weights = weights
|
|
106
|
+
self.cmap = cmap
|
|
107
|
+
self.update()
|
|
108
|
+
|
|
109
|
+
self.GetProperty().SetOpacity(alpha)
|
|
110
|
+
|
|
111
|
+
def update(self, t=0):
|
|
112
|
+
i = t % len(self.weights)
|
|
113
|
+
values = self.weights[i]
|
|
114
|
+
# update z-positions
|
|
115
|
+
points = self.polydata.GetPoints()
|
|
116
|
+
for i, v in enumerate(values):
|
|
117
|
+
x,y,_ = points.GetPoint(i)
|
|
118
|
+
points.SetPoint(i, x, y, v+self.offset)
|
|
119
|
+
# update colors
|
|
120
|
+
normalized = np.true_divide(values, np.abs(self.weights).max())
|
|
121
|
+
normalized = np.subtract(normalized, self.weights.min())
|
|
122
|
+
colors = self.colorArray(normalized, self.cmap)
|
|
123
|
+
self.polydata.GetPointData().SetScalars(colors)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class Tubes(Actor):
|
|
127
|
+
|
|
128
|
+
def __init__(self, centers, vectors, radii, alpha=1, cmap=None):
|
|
129
|
+
tails = centers - vectors/2.
|
|
130
|
+
heads = centers + vectors/2.
|
|
131
|
+
points = np.vstack(zip(tails, heads))
|
|
132
|
+
pairs = np.arange(len(centers)*2).reshape(-1, 2)
|
|
133
|
+
radii = radii.repeat(2)
|
|
134
|
+
|
|
135
|
+
assert (points.size/3. == pairs.size)
|
|
136
|
+
assert (pairs.size == radii.size)
|
|
137
|
+
|
|
138
|
+
self.polydata = vtk.vtkPolyData()
|
|
139
|
+
self.polydata.SetPoints(self.pointArray(points))
|
|
140
|
+
self.polydata.SetLines(self.lineArray(pairs))
|
|
141
|
+
self.polydata.GetPointData().SetScalars(self.floatArray(radii))
|
|
142
|
+
|
|
143
|
+
self.tubeFilter = vtk.vtkTubeFilter()
|
|
144
|
+
self.tubeFilter.SetInput(self.polydata)
|
|
145
|
+
self.tubeFilter.SetVaryRadiusToVaryRadiusByAbsoluteScalar()
|
|
146
|
+
self.tubeFilter.SetNumberOfSides(10)
|
|
147
|
+
self.tubeFilter.CappingOn()
|
|
148
|
+
|
|
149
|
+
self.mapper = vtk.vtkPolyDataMapper()
|
|
150
|
+
self.mapper.SetInputConnection(self.tubeFilter.GetOutputPort())
|
|
151
|
+
self.mapper.ScalarVisibilityOff()
|
|
152
|
+
self.SetMapper(self.mapper)
|
|
153
|
+
|
|
154
|
+
self.GetProperty().SetOpacity(alpha)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class Spheres(Actor):
|
|
158
|
+
|
|
159
|
+
def __init__(self, centers, radii, alpha=1, color=(1,1,1)):
|
|
160
|
+
self.polydata = vtk.vtkPolyData()
|
|
161
|
+
self.polydata.SetPoints(self.pointArray(centers))
|
|
162
|
+
self.radii = np.atleast_2d(radii)
|
|
163
|
+
self.update()
|
|
164
|
+
|
|
165
|
+
self.sphere_source = vtk.vtkSphereSource()
|
|
166
|
+
self.glypher = vtk.vtkProgrammableGlyphFilter()
|
|
167
|
+
self.glypher.SetInput(self.polydata)
|
|
168
|
+
self.glypher.SetSource(self.sphere_source.GetOutput())
|
|
169
|
+
self.glypher.SetGlyphMethod(self.glyph_method)
|
|
170
|
+
|
|
171
|
+
self.mapper = vtk.vtkPolyDataMapper()
|
|
172
|
+
self.mapper.SetInputConnection(self.glypher.GetOutputPort())
|
|
173
|
+
self.SetMapper(self.mapper)
|
|
174
|
+
|
|
175
|
+
self.GetProperty().SetOpacity(alpha)
|
|
176
|
+
r,g,b = color
|
|
177
|
+
self.mapper.SetScalarVisibility(False)
|
|
178
|
+
self.GetProperty().SetColor(r,g,b)
|
|
179
|
+
|
|
180
|
+
def glyph_method(self):
|
|
181
|
+
pid = self.glypher.GetPointId()
|
|
182
|
+
self.sphere_source.SetCenter(self.glypher.GetPoint())
|
|
183
|
+
radius = self.glypher.GetPointData().GetScalars().GetValue(pid)
|
|
184
|
+
self.sphere_source.SetRadius(radius)
|
|
185
|
+
|
|
186
|
+
def update(self, t=0):
|
|
187
|
+
i = t % len(self.radii)
|
|
188
|
+
self.polydata.GetPointData().SetScalars(self.floatArray(self.radii[i]))
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
class Scene(object):
|
|
192
|
+
ticks = count(0)
|
|
193
|
+
|
|
194
|
+
def __init__(self, parent=None, fix_camera=True):
|
|
195
|
+
'''
|
|
196
|
+
fix_camera : more sensible default
|
|
197
|
+
'''
|
|
198
|
+
if parent is not None:
|
|
199
|
+
self.renWin = parent.GetRenderWindow()
|
|
200
|
+
self.iren = self.renWin.GetInteractor()
|
|
201
|
+
else:
|
|
202
|
+
self.renWin = vtk.vtkRenderWindow()
|
|
203
|
+
self.iren = vtk.vtkRenderWindowInteractor()
|
|
204
|
+
self.iren.SetRenderWindow(self.renWin)
|
|
205
|
+
|
|
206
|
+
self.ren = vtk.vtkRenderer()
|
|
207
|
+
self.renWin.AddRenderer(self.ren)
|
|
208
|
+
|
|
209
|
+
if fix_camera:
|
|
210
|
+
camera = vtk.vtkInteractorStyleTrackballCamera()
|
|
211
|
+
self.iren.SetInteractorStyle(camera)
|
|
212
|
+
|
|
213
|
+
def update_all(self, object=None, event=None, t=None):
|
|
214
|
+
if t is None: t = next(self.ticks)
|
|
215
|
+
for aid in range(self.ren.VisibleActorCount()):
|
|
216
|
+
actor = self.ren.GetActors().GetItemAsObject(aid)
|
|
217
|
+
actor.update(t)
|
|
218
|
+
self.renWin.Render()
|
|
219
|
+
|
|
220
|
+
def save(self, frames, outfile='animated.gif'):
|
|
221
|
+
'''
|
|
222
|
+
takes a snapshot of the frames at given t, and returns the paths
|
|
223
|
+
'''
|
|
224
|
+
windowToImage = vtk.vtkWindowToImageFilter()
|
|
225
|
+
windowToImage.SetInput(self.renWin)
|
|
226
|
+
writer = vtk.vtkPNGWriter()
|
|
227
|
+
writer.SetInput(windowToImage.GetOutput())
|
|
228
|
+
|
|
229
|
+
slide_paths = []
|
|
230
|
+
for t in frames:
|
|
231
|
+
f = NamedTemporaryFile(suffix='.png', delete=False)
|
|
232
|
+
self.update_all(t=t)
|
|
233
|
+
windowToImage.Modified()
|
|
234
|
+
writer.SetFileName(f.name)
|
|
235
|
+
writer.Write()
|
|
236
|
+
slide_paths.append( f.name )
|
|
237
|
+
|
|
238
|
+
call(["convert"] + slide_paths + [outfile])
|
|
239
|
+
call(["rm"] + slide_paths)
|
|
240
|
+
|
|
241
|
+
def play(self, timeout=1):
|
|
242
|
+
self.iren.Initialize()
|
|
243
|
+
if timeout is not None:
|
|
244
|
+
self.iren.AddObserver('TimerEvent', self.update_all)
|
|
245
|
+
self.timer = self.iren.CreateRepeatingTimer(timeout)
|
|
246
|
+
self.update_all()
|
|
247
|
+
self.iren.Start()
|
|
248
|
+
|
|
249
|
+
def add_actors(self, list_of_actors):
|
|
250
|
+
for actor in list_of_actors:
|
|
251
|
+
self.ren.AddActor(actor)
|