tadd plotContacts and visualize('contacts') - sphere - GPU-based 3D discrete element method algorithm with optional fluid coupling
(HTM) git clone git://src.adamsgaard.dk/sphere
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) LICENSE
---
(DIR) commit 5c8428450cf432320ac95a9cecc9f712f6acdd33
(DIR) parent 9972531e74fd32ad4882082b820f0723a1e533ea
(HTM) Author: Anders Damsgaard <anders.damsgaard@geo.au.dk>
Date: Wed, 8 Apr 2015 15:31:36 +0200
add plotContacts and visualize('contacts')
Diffstat:
M python/sphere.py | 126 ++++++++++++++++++++++++++++++-
1 file changed, 125 insertions(+), 1 deletion(-)
---
(DIR) diff --git a/python/sphere.py b/python/sphere.py
t@@ -5216,6 +5216,117 @@ class sim:
graphics_format)
fig.clf()
+ def plotContacts(self, graphics_format = 'png', figsize=[6,6], title=None,
+ lower_limit = 0.0, upper_limit = 1.0, alpha=1.0, return_data=False,
+ outfolder='.'):
+ '''
+ Plot current contact orientations on polar plot
+
+ :param lower_limit: Do not visualize force chains below this relative
+ contact force magnitude, in ]0;1[
+ :type lower_limit: float
+ :param upper_limit: Visualize force chains above this relative
+ contact force magnitude but cap color bar range, in ]0;1[
+ :type upper_limit: float
+ :param graphics_format: Save the plot in this format
+ :type graphics_format: str
+ '''
+ self.writebin(verbose=False)
+
+
+ subprocess.call("cd .. && ./forcechains -f txt input/" + self.sid \
+ + ".bin > python/contacts-tmp.txt", shell=True)
+
+ # data will have the shape (numcontacts, 7)
+ data = numpy.loadtxt('contacts-tmp.txt', skiprows=1)
+
+ # find the max. value of the normal force
+ f_n_max = numpy.amax(data[:,6])
+
+ # specify the lower limit of force chains to do statistics on
+ f_n_lim = lower_limit * f_n_max
+
+ # find the indexes of these contacts
+ I = numpy.nonzero(data[:,6] >= f_n_lim)
+
+ # loop through these contacts and find the strike and dip of the
+ # contacts
+ strikelist = [] # strike direction of the normal vector, [0:360[
+ diplist = [] # dip of the normal vector, [0:90]
+ forcemagnitude = data[I,6]
+ for i in I[0]:
+
+ x1 = data[i,0]
+ y1 = data[i,1]
+ z1 = data[i,2]
+ x2 = data[i,3]
+ y2 = data[i,4]
+ z2 = data[i,5]
+
+ if z1 < z2:
+ xlower = x1; ylower = y1; zlower = z1
+ xupper = x2; yupper = y2; zupper = z2
+ else :
+ xlower = x2; ylower = y2; zlower = z2
+ xupper = x1; yupper = y1; zupper = z1
+
+ # Vector pointing downwards
+ dx = xlower - xupper
+ dy = ylower - yupper
+ dz = zlower - zupper
+ dhoriz = numpy.sqrt(dx**2 + dy**2)
+
+ # Find dip angle
+ diplist.append(numpy.degrees(numpy.arctan((zupper - zlower)/dhoriz)))
+
+ # Find strike angle
+ if ylower >= yupper: # in first two quadrants
+ strikelist.append(numpy.arccos(dx/dhoriz))
+ else :
+ strikelist.append(2.0*numpy.pi - numpy.arccos(dx/dhoriz))
+
+ plt.figure(figsize=figsize)
+ ax = plt.subplot(111, polar=True, axisbg='white')
+ cs = ax.scatter(strikelist, diplist, marker='o',
+ c=forcemagnitude,
+ s=forcemagnitude/f_n_max*40.,
+ alpha=alpha,
+ edgecolors='none',
+ vmin=f_n_max*lower_limit,
+ vmax=f_n_max*upper_limit,
+ cmap=matplotlib.cm.get_cmap('afmhot_r'))
+ plt.colorbar(cs, extend='max')
+
+ # plot defined max compressive stress from tau/N ratio
+ ax.scatter(0., # prescribed stress
+ numpy.degrees(numpy.arctan(
+ self.shearStress('defined')/
+ self.currentNormalStress('defined'))),
+ marker='o', c='none', s=300)
+ ax.scatter(0., # actual stress
+ numpy.degrees(numpy.arctan(
+ self.shearStress('effective')/
+ self.currentNormalStress('effective'))),
+ marker='+', color='k', s=300)
+
+ ax.set_rmax(90)
+ ax.set_rticks([])
+
+ if title:
+ plt.title(title)
+ else:
+ plt.title('t = {:.2f} s'.format(self.currentTime()))
+
+ #plt.tight_layout()
+ plt.savefig(outfolder + '/' + self.sid + '-contacts.' + \
+ graphics_format,\
+ transparent=False)
+
+ subprocess.call('rm contacts-tmp.txt', shell=True)
+
+ if return_data:
+ return data
+
def plotFluidPressuresY(self, y = -1, graphics_format = 'png'):
'''
Plot fluid pressures in a plane normal to the second axis.
t@@ -5876,7 +5987,7 @@ class sim:
:param method: The type of plot to render. Possible values are 'energy',
'walls', 'triaxial', 'inertia', 'mean-fluid-pressure',
'fluid-pressure', 'shear', 'shear-displacement', 'porosity',
- 'rate-dependence'
+ 'rate-dependence', 'contacts'
:type method: str
:param savefig: Save the image instead of showing it on screen
:type savefig: bool
t@@ -6738,6 +6849,19 @@ class sim:
plt.tight_layout()
plt.subplots_adjust(wspace = .05)
+ elif method == 'contacts':
+
+ for i in numpy.arange(sb.status()+1):
+ fn = "../output/{0}.output{1:0=5}.bin".format(self.sid, i)
+ sb.sid = self.sid + ".{:0=5}".format(i)
+ sb.readbin(fn, verbose=True)
+ sb.plotContacts(lower_limit=0.25, upper_limit=0.75,
+ outfolder = '../img_out/')
+
+ subprocess.call('cd ../img_out/ && ' +
+ 'convert -quality 100 {}.*.png {}-contacts.avi',
+ shell=True)
+
else:
print("Visualization type '" + method + "' not understood")
return