/usr/lib/python2.7/dist-packages/woo/pre/cylTriax.py is in python-woo 1.0+dfsg1-1+b4.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 | # encoding: utf-8
from woo.dem import *
from woo.fem import *
import woo.core
import woo.dem
import woo.pyderived
import woo.pack
import woo
import math
from minieigen import *
import numpy
class CylTriaxTest(woo.core.Preprocessor,woo.pyderived.PyWooObject):
r'''
Preprocessor for cylindrical triaxial test with membrane. The test is run in 3 stages:
* compaction, where random loose packing of spheres is compressed to attain the :math:`\sigma_{\rm iso}` (:obj:`sigIso`) pressure in all directions; during this stage, the cylindrical boundary is rigid and resized along main axes (so it can become (slightly) elliptical); friction is turned off during this stage to achieve better compacity; the compaction finishes when stress level is sufficiently close to the desired one, and unbalanced force drops below :obj:`maxUnbalanced`.
* Membrane stabilization: once the compression is done, membrane around the cylinder is activated -- loaded with surface pressure and made flexible. Friction is activated at this moment. The cylinder may deform axially (stress-controlled), but lateral deformation is now due to membrane-particle interaction. This stage finishes when unbalanced force drops below 1/10th of :obj:`maxUnbalanced` (the reason is that membrane motion is not considered when computing unbalanced force, only mononodal particles are). Surface pressure is adjusted so that the value of lateral stress (in terms of global stress tensor) is close to :obj:`sigIso`. At the same time, friction is increased from initial zero values
* Triaxial compression: displacement-controlled compression along the ``z`` axis, with strain rate increasing until :obj:`maxRates` is reached; the test finishes when axial strain attains :obj:`stopStrain`; during the triaxial phase, lateral pressure is exerted by surface load of membrane elements.
Membrane thickness :obj:`memThick` should be set carefully. The article :cite:`Molenkamp1981` discusses membrane thickness relative to maximum grain size, depending on the ratio of grain stiffness and applied stress.
Supports are from the same material as *particles*, but they may have their friction reduced (when :obj:`suppTanPhi` is given).
.. warning:: There are (unfortunately) quite a few tunables which must be tinkered with to get the desired result (those are in the *Tunables* section: :obj:`dtSafety`, :obj:`massFactor`, :obj:`model.damping`, :obj:`maxUnbalanced`). Several factors are also hard-set in the code, hoping that they will work in different scenarios than those which were tested.
.. youtube:: Li13NrIyMYU
'''
_classTraits=None
_PAT=woo.pyderived.PyAttrTrait # less typing
_attrTraits=[
##
_PAT(Vector2,'htDiam',Vector2(.06,.04),unit='m',startGroup='Geometry & control',doc='Initial size of the cylinder (radius and height)'),
_PAT(float,'memThick',-1.0,unit='m',doc='Membrane thickness; if negative, relative to largest particle diameter'),
_PAT(float,'cylDiv',40,'Number of segments for cylinder (first component)'),
_PAT(float,'sigIso',-500e3,unit='Pa',doc='Isotropic compaction stress, and lateral stress during the triaxial phase'),
_PAT(float,'stopStrain',-.2,unit=r'%',doc='Goal value of axial deformation in the triaxial phase'),
_PAT(Vector2,'maxRates',(2e-1,1.),'Maximum strain rates during the compaction phase (for all axes), and during the triaxial phase in the axial sense.'),
## materials
_PAT(
woo.models.ContactModelSelector,'model',woo.models.ContactModelSelector(name='linear',damping=.5,numMat=(1,2),matDesc=['particles','membrane'],mats=[
FrictMat(young=0.3e9,ktDivKn=.2,tanPhi=.4,density=1e8),
FrictMat(young=1.1e6,ktDivKn=.2,tanPhi=.4,density=1e8)
]),
startGroup='Materials',
doc='Select contact model. The first material is for particles; the second, optional, material, is for the membrane (the first material is used if there is no second one, but its friction is nevertheless reduced during the compaction phase to :obj:`suppTanPhi`).'
),
_PAT([Vector2,],'psd',[(2e-3,0),(2.5e-3,.2),(4e-3,1.)],unit=['mm','%'],psd=True,doc='Particle size distribution of particles; first value is diameter, scond is cummulative mass fraction.'),
_PAT([woo.dem.SphereClumpGeom],'clumps',[],"Clump definitions (if empty, use spheres, not clumps)"),
_PAT(str,'spheresFrom','',existingFilename=True,doc='Instead of generating spheres, load them from file (space-separated colums with x,y,z,r entries). The initial cylinder is made to fit inside the packing\'s axis-aligned bounding box (the user is responsible for having those spheres inside cylinder). Cylinder geometry (:obj:`htDiam`) and particle sizes (:obj:`psd` and :obj:`clumps`) are ignored.\n\n.. note:: :obj:`packCacheDir` is still used as usual to cache packings after compaction (to disable packing cache, set it to empty string), and will take precedence over :obj:`spheresFrom` if compacted packing for the same parameters is already cached.'),
_PAT(float,'suppTanPhi',float('nan'),'Friction at supports; if NaN, the same as for particles is used. Supports use the same material as particles otherwise.'),
## output
_PAT(str,'reportFmt',"/tmp/{tid}.xhtml",filename=True,startGroup="Outputs",doc="Report output format; :obj:`Scene.tags <woo.core.Scene.tags>` can be used."),
_PAT(str,'packCacheDir',".",dirname=True,doc="Directory where to store pre-generated feed packings; if empty, packing wil be re-generated every time."),
_PAT(str,'saveFmt',"/tmp/{tid}-{stage}.bin.gz",filename=True,doc='Savefile format; keys are :obj:`Scene.tags <woo.core.Scene.tags>`; additionally ``{stage}`` will be replaced by ``pre-triax`` after membrane stabilization (right before the triaxial compression actually starts) and ``done`` at the very end.'),
_PAT(int,'vtkStep',0,'Periodicity of saving VTK exports'),
_PAT(str,'vtkFmt','/tmp/{title}.{id}-',filename=True,doc='Prefix for VTK exports'),
#_PAT(int,'backupSaveTime',1800,doc='How often to save backup of the simulation (0 or negative to disable)'),
## tunables
_PAT(float,'dtSafety',.9,startGroup='Tunables',doc='Safety factor, stored in :obj:`woo.core.Scene.dtSafety` and used for computing the initial timestep as well as by :obj:`woo.dem.DynDt` later during the simulation.'),
_PAT(float,'massFactor',.5,'Multiply real mass of particles by this number to obtain the :obj:`woo.dem.WeirdTriaxControl.mass` control parameter'),
_PAT(float,'maxUnbalanced',.05,'Maximum unbalanced force at the end of compaction'),
]
def __init__(self,**kw):
woo.core.Preprocessor.__init__(self)
self.wooPyInit(self.__class__,woo.core.Preprocessor,**kw)
def __call__(self):
# preprocessor builds the simulation when called
return prepareCylTriax(self)
def mkFacetCyl(aabb,cylDiv,suppMat,sideMat,suppMask,sideMask,suppBlock,sideBlock,sideThick,mass,inertia):
'Make closed cylinder from facets. Z is axis of the cylinder. The position is determined by aabb; the cylinder may be elliptical, if the x and y dimensions are different. Return list of particles and list of nodes. The first two nodes in the list are bottom central node and top central node. cylDiv is tuple specifying division in circumferential and axial direcrtion respectively.'
r1,r2=.5*aabb.sizes()[0],.5*aabb.sizes()[1]
C=aabb.center()
zMin,zMax=aabb.min[2],aabb.max[2]
centrals=[woo.core.Node(pos=Vector3(C[0],C[1],zMin)),woo.core.Node(pos=Vector3(C[0],C[1],zMax))]
for c in centrals:
c.dem=woo.dem.DemData()
c.dem.mass=mass
c.dem.inertia=inertia
c.dem.blocked='xyzXYZ'
retParticles=[]
# nodes on the perimeter
thetas=numpy.linspace(2*math.pi,0,num=cylDiv[0],endpoint=False)
xxyy=[Vector2(r1*math.cos(th)+C[0],r2*math.sin(th)+C[1]) for th in thetas]
zz=numpy.linspace(zMin,zMax,num=cylDiv[1],endpoint=True)
nnn=[[woo.core.Node(pos=Vector3(xy[0],xy[1],z)) for xy in xxyy] for z in zz]
for i,nn in enumerate(nnn):
if i==0 or i==(len(nnn)-1): blocked=suppBlock
else: blocked=sideBlock
for n in nn:
n.dem=woo.dem.DemData()
n.dem.mass=mass
n.dem.inertia=inertia
n.dem.blocked=blocked
def mkCap(nn,central,mask,mat):
ret=[]
NaN=float('nan')
for i in range(len(nn)):
# with NaN in fakeVel[0], local linear (interpolated) velocity is zero even if nodes move
# that's what we need at supports, which stretch to the membrane's edge,
# but that is not any physical motion
ret.append(woo.dem.Particle(material=mat,shape=Facet(nodes=[nn[i],nn[(i+1)%len(nn)],central],fakeVel=Vector3(NaN,NaN,NaN)),mask=mask))
nn[i].dem.addParRef(ret[-1])
nn[(i+1)%len(nn)].dem.addParRef(ret[-1])
central.dem.addParRef(ret[-1])
return ret
retParticles+=mkCap(nnn[0],central=centrals[0],mask=suppMask,mat=suppMat)
retParticles+=mkCap(list(reversed(nnn[-1])),central=centrals[-1],mask=suppMask,mat=suppMat) # reverse to have normals outside
def mkAround(nnAC,nnBD,mask,mat,halfThick):
ret=[]
for i in range(len(nnAC)):
A,B,C,D=nnAC[i],nnBD[i],nnAC[(i+1)%len(nnAC)],nnBD[(i+1)%len(nnBD)]
ret+=[woo.dem.Particle(material=mat,shape=Membrane(nodes=fNodes,halfThick=halfThick),mask=mask) for fNodes in ((A,B,D),(A,D,C))]
for n in (A,B,D): n.dem.addParRef(ret[-2])
for n in (A,D,C): n.dem.addParRef(ret[-1])
return ret
for i in range(0,len(nnn)-1):
retParticles+=mkAround(nnn[i],nnn[i+1],mask=sideMask,mat=sideMat,halfThick=.5*sideThick)
for p in retParticles: p.shape.wire=True
import itertools
return retParticles,centrals+list(itertools.chain.from_iterable(nnn))
def prepareCylTriax(pre):
import woo
margin=.6
rad,ht=.5*pre.htDiam[1],pre.htDiam[0]
bot,top=margin*ht,(1+margin)*ht
xymin=Vector2(margin*rad,margin*rad)
xymax=Vector2((margin+2)*rad,(margin+2)*rad)
xydomain=Vector2((2*margin+2)*rad,(2*margin+2)*rad)
xymid=.5*xydomain
S=woo.core.Scene(fields=[DemField()])
for a in ['reportFmt','packCacheDir','saveFmt','vtkFmt']: setattr(pre,a,woo.utils.fixWindowsPath(getattr(pre,a)))
S.pre=pre.deepcopy()
S.periodic=True
S.cell.setBox(xydomain[0],xydomain[1],(2*margin+1)*ht)
meshMask=0b0011
spheMask=0b0001
loneMask=0b0010
S.dem.loneMask=loneMask
# save materials for later manipulation
S.lab.parMat=pre.model.mats[0]
S.lab.memMat=(pre.model.mats[1] if len(pre.model.mats)>1 else pre.model.mats[0].deepcopy())
S.lab.suppMat=pre.model.mats[0].deepcopy()
S.lab.suppMat.tanPhi=pre.suppTanPhi
## generate particles inside cylinder
# radius minus polygonal imprecision (circle segment), minus halfThickness of the membrane
if pre.memThick<0: pre.memThick*=-pre.psd[-1][0]
innerRad=rad-rad*(1.-math.cos(.5*2*math.pi/pre.cylDiv))-.5*pre.memThick
S.lab.memThick=pre.memThick
if pre.packCacheDir:
import hashlib,os
compactMemoize=pre.packCacheDir+'/'+hashlib.sha1(pre.dumps(format='expr')+'ver3').hexdigest()+'.triax-compact'
print 'Compaction memoize file is ',compactMemoize
else: compactMemoize='' # no memoize file
if pre.packCacheDir and os.path.exists(compactMemoize):
print 'Using memoized compact state'
sp=woo.pack.SpherePack()
sp.load(compactMemoize)
meshAabb=eval(sp.userData)
S.lab.compactMemoize=None # none means we just loaded that file
sp.toSimulation(S,mat=S.lab.parMat)
else:
if pre.spheresFrom:
pack=woo.pack.SpherePack()
pack.load(pre.spheresFrom)
else:
pack=woo.pack.randomLoosePsd(predicate=woo.pack.inCylinder(centerBottom=(xymid[0],xymid[1],bot),centerTop=(xymid[0],xymid[1],top),radius=innerRad),psd=pre.psd,mat=S.lab.parMat,clumps=pre.clumps,returnSpherePack=True)
pack.toSimulation(S)
meshAabb=AlignedBox3((xymin[0],xymin[1],bot),(xymax[0],xymax[1],top))
S.lab.compactMemoize=compactMemoize
sumParMass=sum([p.mass for p in S.dem.par])
# create mesh (supports+membrane)
cylDivHt=int(round(ht/(2*math.pi*rad/pre.cylDiv))) # try to be as square as possible
nodeMass=(ht/cylDivHt)**2*pre.memThick*S.lab.memMat.density # approx mass of square slab of our size
nodeInertia=((3/4.)*(nodeMass/math.pi))**(5/3.)*(6/15.)*math.pi # inertial of sphere with the same mass
particles,nodes=mkFacetCyl(
aabb=meshAabb,
cylDiv=(pre.cylDiv,cylDivHt),
suppMask=meshMask,sideMask=meshMask,
sideBlock='xyzXYZ',suppBlock='xyzXYZ',
mass=nodeMass,inertia=nodeInertia*Vector3(1,1,1),
suppMat=S.lab.suppMat,sideMat=S.lab.memMat,
sideThick=pre.memThick,
)
S.lab.cylNodes=nodes
S.dem.par.add(particles)
##
# collect nodes from both facets and spheres
S.dem.collectNodes()
#S.dt=pre.pWaveSafety*woo.utils.pWaveDt(S,noClumps=True)
S.dtSafety=pre.dtSafety
if pre.clumps: print 'WARNING: clumps used, Scene.dt might not be correct; lower CylTriaxTest.dtSafety (currently %g) if the simulation is unstable'%(pre.dtSafety)
# setup engines
S.engines=[
WeirdTriaxControl(goal=(pre.sigIso,pre.sigIso,pre.sigIso),maxStrainRate=(pre.maxRates[0],pre.maxRates[0],pre.maxRates[0]),relVol=math.pi*innerRad**2*ht/S.cell.volume,stressMask=0b0111,maxUnbalanced=pre.maxUnbalanced,mass=pre.massFactor*sumParMass,doneHook='import woo.pre.cylTriax; woo.pre.cylTriax.compactionDone(S)',label='triax',absStressTol=1e4,relStressTol=1e-2),
]+DemField.minimalEngines(model=pre.model,lawKw=dict(noFrict=True))+[
#InsertionSortCollider([Bo1_Sphere_Aabb(),Bo1_Facet_Aabb()],verletDist=-.05),
#ContactLoop(
# [Cg2_Sphere_Sphere_L6Geom(),Cg2_Facet_Sphere_L6Geom()],
# [Cp2_FrictMat_FrictPhys()],
# [Law2_L6Geom_FrictPhys_IdealElPl(noFrict=True,label='contactLaw')],
# applyForces=True,label='contactLoop'
#),
IntraForce([
In2_Sphere_ElastMat(),
In2_Membrane_ElastMat(bending=True)],
label='intraForce',dead=True # ContactLoop applies forces during compaction
),
# run the same as addPlotData
MeshVolume(mask=S.dem.loneMask,stepPeriod=20,label='meshVolume',dead=False),
woo.core.PyRunner(20,'import woo.pre.cylTriax; woo.pre.cylTriax.addPlotData(S)'),
VtkExport(out=pre.vtkFmt,stepPeriod=pre.vtkStep,what=VtkExport.all,dead=True,label='vtk'),
#Leapfrog(damping=pre.damping,reset=True,label='leapfrog'),
#DynDt(stepPeriod=500),
]
S.lab.stage='compact'
## if spheres were loaded externally, compaction is done just now
##
if S.lab.compactMemoize==None: compactionDone(S)
try:
import woo.gl
S.any=[woo.gl.Renderer(dispScale=(5,5,2),rotScale=0,cell=False),woo.gl.Gl1_DemField(),woo.gl.Gl1_CPhys(),woo.gl.Gl1_Membrane(phiSplit=False,phiWd=1,relPhi=0.,uScale=0.,slices=-1,wire=True),woo.gl.Gl1_Facet(wd=2,slices=-1)]
except ImportError: pass
return S
def addPlotData(S):
assert S.lab.stage in ('compact','stabilize','triax')
import woo
t=S.lab.triax
# global stress tensor
sxx,syy,szz=t.stress.diagonal()
dotE=S.cell.gradV.diagonal()
dotEMax=t.maxStrainRate
# net volume, without membrane thickness
vol=S.lab.meshVolume.netVol
# current radial stress
srr=.5*(sxx+syy)
# mean stress
p=t.stress.diagonal().sum()/3.
# deviatoric stress
q=szz-.5*(sxx+syy)
qDivP=(q/p if p!=0 else float('nan'))
if S.lab.stage in ('compact','stabilize'):
## t.strain is log(l/l0) for all components
exx,eyy,ezz=t.strain
err=.5*(exx+eyy)
# volumetric strain is not defined directly, and it is not needed either
eVol=float('nan')
else:
# triaxial phase:
# only axial strain (ezz) and volumetric strain (eVol) are known
#
# set the initial volume, if not yet done
if not hasattr(S.lab,'netVol0'): S.lab.netVol0=S.lab.meshVolume.netVol
# axial strain is known; xy components irrelevant (inactive)
ezz=t.strain[2]
# current net volume / initial net volume
eVol=math.log(vol/S.lab.netVol0)
# radial strain
err=.5*(eVol-ezz)
# undefined
exx=eyy=float('nan')
# deviatoric strain
eDev=ezz-(1/3.)*(2*err+ezz) # FIXME: is this correct?!
surfLoad=(float('nan') if S.lab.stage=='compact' else S.lab.surfLoad)
S.plot.addData(unbalanced=woo.utils.unbalancedForce(),i=S.step,
sxx=sxx,syy=syy,
srr=.5*(sxx+syy),szz=szz,
surfLoad=surfLoad,
exx=exx,eyy=eyy,
err=err,ezz=ezz,
dotE=dotE,dotErr=.5*(dotE[0]+dotE[1]),
dotEMax=dotEMax,
dotEMax_z_neg=-dotEMax[2],
eDev=eDev,eVol=eVol,
vol=vol,
p=p,q=q,qDivP=qDivP,
isTriax=(1 if S.lab.stage=='triax' else 0), # to be able to filter data
grossVol=S.lab.meshVolume.vol,
parTanPhi=S.lab.parMat.tanPhi,
memTanPhi=S.lab.memMat.tanPhi,
suppTanPhi=S.lab.suppMat.tanPhi
# save all available energy data
#Etot=O.energy.total()#,**O.energy
)
if not S.plot.plots:
S.plot.plots={
'i':('unbalanced',None,'parTanPhi','memTanPhi','suppTanPhi'),'i ':('srr','szz','surfLoad'),' i':('err','ezz','eVol'),' i':('vol','grossVol'),'i ':('dotE_z','dotEMax_z','dotEMax_z_neg')
# energy plot
#' i ':(O.energy.keys,None,'Etot'),
}
S.plot.xylabels={'i ':('step','Stress [Pa]',),' i':('step','Strains [-]','Strains [-]')}
S.plot.labels={
'sxx':r'$\sigma_{xx}$','syy':r'$\sigma_{yy}$','szz':r'$\sigma_{zz}$','srr':r'$\sigma_{rr}$','surfLoad':r'$\sigma_{\rm hydro}$',
'exx':r'$\varepsilon_{xx}$','eyy':r'$\varepsilon_{yy}$','ezz':r'$\varepsilon_{zz}$','err':r'$\varepsilon_{rr}$','eVol':r'$\varepsilon_{v}$','vol':'net volume','grossVol':'midplane volume',
'dotE_x':r'$\dot\varepsilon_{xx}$','dotE_y':r'$\dot\varepsilon_{yy}$','dotE_z':r'$\dot\varepsilon_{zz}$','dotE_rr':r'$\dot\varepsilon_{rr}$','dotEMax_z':r'$\dot\varepsilon_{zz}^{\rm max}$','dotEMax_z_neg':r'$-\dot\varepsilon_{zz}^{\rm max}$'
}
if S.lab.meshVolume.netVol>0:
S.lab.triax.relVol=S.lab.meshVolume.netVol/S.cell.volume
if S.lab.stage=='stabilize':
stable=True
#
# adjust surface load so that we approach the right value
#
sl=S.lab.surfLoad-.002*(srr-S.pre.sigIso)
#print 'Old',S.lab.surfLoad,'new',sl,'(desired',S.pre.sigIso,'current',srr,')'
del S.lab.surfLoad
S.lab.surfLoad=sl
#print 'Changing surface load to ',S.lab.surfLoad,', srr is',srr
for p in S.dem.par:
if isinstance(p.shape,Membrane): p.shape.surfLoad=S.lab.surfLoad
## 2% tolerance on stress
if (srr-S.pre.sigIso)/abs(S.pre.sigIso)>2e-2: stable=False
for m,tp in [(S.lab.parMat,S.lab.parTanPhi),(S.lab.memMat,S.lab.memTanPhi),(S.lab.suppMat,S.lab.suppTanPhi)]:
if m.tanPhi<tp:
m.tanPhi=min(m.tanPhi+.002*tp,tp)
stable=False
# once the membrane is stabilized, decrease strain rate as well
if stable:
# decrease max strain rate along z
# to avoid gross oscillations
if t.maxStrainRate[2]>.01*S.pre.maxRates[1]:
t.maxStrainRate[2]=max(t.maxStrainRate[2]-.01*S.pre.maxRates[0],.01*S.pre.maxRates[1])
stable=False
## don't do this, can take forever
# and then wait for strain rate to drop what will be applied next
# we go down to 1/1000th, that's where we start during the triaxial test then...
# wait for strain rate to settle down
# if abs(S.cell.gradV[2,2])>.001*S.pre.maxRates[1]: stable=False
# green light for triax to finish
if stable: S.lab.triax.goal[0]=0
if S.lab.stage=='triax':
t.maxStrainRate[2]=min(t.maxStrainRate[2]+.001*S.pre.maxRates[1],S.pre.maxRates[1])
def velocityFieldPlots(S,nameBase):
import woo
from woo import post2d
flattener=post2d.CylinderFlatten(useRef=False,axis=2,origin=(.5*S.cell.size[0],.5*S.cell.size[1],(.6/2.2*S.cell.size[2])))
#maxVel=float('inf') #5e-2
#exVel=lambda p: p.vel if p.vel.norm()<=maxVel else p.vel/(p.vel.norm()/maxVel)
exVel=lambda p: p.vel
exVelNorm=lambda p: exVel(p).norm()
from matplotlib.figure import Figure
fVRaw=Figure(); ax=fVRaw.add_subplot(1,1,1)
post2d.plot(post2d.data(S,exVel,flattener),axes=ax,alpha=.3,minlength=.3,cmap='jet')
fV2=Figure(); ax=fV1.add_subplot(1,1,1)
post2d.plot(post2d.data(S,exVel,flattener,stDev=.5*S.pre.psd[0][0],div=(80,80)),axes=ax,minlength=.6,cmap='jet')
fV1=Figure(); ax=fV1.add_subplot(1,1,1)
post2d.plot(post2d.data(S,exVelNorm,flattener,stDev=.5*S.pre.psd[0][0],div=(80,80)),axes=ax,cmap='jet')
outs=[]
for name,fig in [('particle-velocity',fVRaw),('smooth-velocity',fV2),('smooth-velocity-norm',fV1)]:
out=nameBase+'.%s.png'%name
fig.savefig(out)
outs.append(out)
return outs
def membraneStabilized(S):
print 'Membrane stabilized at step',S.step,'with surface pressure',S.lab.surfLoad
S.lab.triax.goal=(0,0,S.pre.stopStrain)
S.lab.triax.stressMask=0b0000 # all strain-controlled
S.lab.triax.maxStrainRate=(0,0,.001*S.pre.maxRates[1])
S.lab.triax.maxUnbalanced=10 # don't care, just compress until done
S.lab.leapfrog.damping=S.pre.damping
S.lab.triax.doneHook='import woo.pre.cylTriax; woo.pre.cylTriax.triaxDone(S)'
# this is the real ref config now
S.cell.trsf=Matrix3.Identity
S.cell.refHSize=S.cell.hSize
try:
import woo.gl
woo.gl.Gl1_DemField.updateRefPos=True
except ImportError: pass
# not sure if this does any good actually
for n in S.dem.nodes: n.dem.vel=n.dem.angVel=Vector3.Zero
del S.lab.stage # avoid warning
S.lab.stage='triax'
# this is no longer needed, tanPhi is constant now
S.lab.contactLoop.updatePhys=False
if S.pre.saveFmt:
out=S.pre.saveFmt.format(stage='pre-triax',S=S,**(dict(S.tags)))
print 'Saving to',out
S.save(out)
def compactionDone(S):
if S.lab.compactMemoize: print 'Compaction done at step',S.step
import woo
t=S.lab.triax
# set the current cell configuration to be the reference one
S.cell.trsf=Matrix3.Identity
S.cell.refHSize=S.cell.hSize
t.maxUnbalanced=.1*S.pre.maxUnbalanced # need more stability for triax?
S.lab.leapfrog.damping=.7 # increase damping to a larger value
t.goal=(1,0,S.pre.sigIso) # this will be set to 0 once all friction angles are OK
t.maxStrainRate=(0,0,S.pre.maxRates[0])
t.doneHook='import woo.pre.cylTriax; woo.pre.cylTriax.membraneStabilized(S)'
t.stressMask=0b0100 # z is stress-controlled, xy strain-controlled
# allow faster deformation along x,y to better maintain stresses
# make the membrane flexible: apply force on the membrane
S.lab.contactLoop.applyForces=False
S.lab.intraForce.dead=False
S.lab.meshVolume.dead=False
S.lab.vtk.dead=(S.pre.vtkStep>0 and S.pre.vtkFmt!='')
# free the nodes
top,bot=S.lab.cylNodes[:2]
tol=1e-3*abs(top.pos[2]-bot.pos[2])
for n in S.lab.cylNodes[2:]:
# supports may move in-plane, and also may rotate
if abs(n.pos[2]-top.pos[2])<tol or abs(n.pos[2]-bot.pos[2])<tol:
n.dem.blocked='z'
else: n.dem.blocked=''
# add surface load
S.lab.surfLoad=S.pre.sigIso*(1-(.5*S.lab.memThick)/(.5*S.pre.htDiam[1]))
print 'Initial surface load',S.lab.surfLoad
for p in S.dem.par:
if isinstance(p.shape,Membrane): p.shape.surfLoad=S.lab.surfLoad
# set velocity to 0 (so that when loading packing, the conditions are the same)
for n in S.dem.nodes: n.dem.vel=n.dem.angVel=Vector3.Zero
# restore friction: friction dissipates a lot of energy, and also creates stress decrease along +z
# which we want to have here, not when the membrane is already stable and the triax itself starts
S.lab.contactLaw.noFrict=False
S.lab.contactLoop.updatePhys=True # force updating CPhys at every step
# save desired values of friction angle
S.lab.parTanPhi=S.lab.parMat.tanPhi
S.lab.memTanPhi=S.lab.memMat.tanPhi
S.lab.suppTanPhi=S.lab.suppMat.tanPhi
# and set it to zero
S.lab.parMat.tanPhi=S.lab.memMat.tanPhi=S.lab.suppMat.tanPhi=0
if S.lab.compactMemoize: # if None or '', don't save
#S.save('/tmp/compact.gz')
aabb=AlignedBox3()
for n in S.lab.cylNodes: aabb.extend(n.pos)
sp=woo.pack.SpherePack()
sp.fromSimulation(S)
sp.userData=str(aabb)
sp.save(S.lab.compactMemoize)
print 'Saved compacted packing to',S.lab.compactMemoize
del S.lab.stage # avoid warning
S.lab.stage='stabilize'
def plotBatchResults(db,titleRegex=None,out=None,stressPath=True,sorter=None):
'Hook called from woo.batch.writeResults'
import re,math,woo.batch,os
from matplotlib.figure import Figure
from matplotlib.backends.backend_agg import FigureCanvasAgg
results=woo.batch.dbReadResults(db)
if sorter: results=sorter(results)
if out==None: out='%s.pdf'%re.sub('\.results$','',db)
from matplotlib.ticker import FuncFormatter
kiloPascal=FuncFormatter(lambda x,pos=0: '%g'%(1e-3*x))
percent=FuncFormatter(lambda x,pos=0: '%g'%(1e2*x))
if stressPath:
fig1,fig2,fig3=311,312,313
fig=Figure(figsize=(8,20))
else:
fig1,fig2=121,122
fig=Figure(figsize=(8,4))
fig.subplots_adjust(left=.1,right=.98,bottom=.15,top=.9,wspace=.2,hspace=.25)
canvas=FigureCanvasAgg(fig)
ed_qp=fig.add_subplot(fig1)
ed_qp.set_xlabel(r'$\varepsilon_d$ [%]')
ed_qp.set_ylabel(r'$q/p$')
ed_qp.xaxis.set_major_formatter(percent)
ed_qp.grid(True)
ed_ev=fig.add_subplot(fig2)
ed_ev.set_xlabel(r'$\varepsilon_d$ [%]')
ed_ev.set_ylabel(r'$\varepsilon_v$ [%]')
ed_ev.xaxis.set_major_formatter(percent)
ed_ev.yaxis.set_major_formatter(percent)
ed_ev.grid(True)
if stressPath:
p_q=fig.add_subplot(fig3)
p_q.set_xlabel(r'$p$ [kPa]')
p_q.set_ylabel(r'$q$ [kPa]')
p_q.xaxis.set_major_formatter(kiloPascal)
p_q.yaxis.set_major_formatter(kiloPascal)
p_q.grid(True)
titlesSkipped,titlesIncluded=[],[]
for res in results:
series,pre=res['series'],res['pre']
title=res['title'] if res['title'] else res['sceneId']
if titleRegex and not re.match(titleRegex,title):
titlesSkipped.append(title)
continue
titlesIncluded.append(title)
isTriax=series['isTriax']
# skip the very first number, since that's the transitioning step and strains are still at their old value
ed=series['eDev'][isTriax==1][1:]
ev=series['eVol'][isTriax==1][1:]
p=series['p'][isTriax==1][1:]
q=series['q'][isTriax==1][1:]
qDivP=series['qDivP'][isTriax==1][1:]
ed_qp.plot(ed,qDivP,label=title,alpha=.6)
ed_ev.plot(ed,ev,label=title,alpha=.6)
if stressPath:
p_q.plot(p,q,label=title,alpha=.6)
if not titlesIncluded:
raise RuntimeError('No simulations in %s%s found.'%(db,(' matching %s'%titleRegex if titleRegex else '')))
ed_qp.invert_xaxis()
ed_ev.invert_xaxis()
ed_ev.invert_yaxis()
if stressPath:
p_q.invert_xaxis()
p_q.invert_yaxis()
for ax,loc in [(ed_qp,'lower right'),(ed_ev,'lower right')]+([(p_q,'upper left')] if stressPath else []):
l=ax.legend(loc=loc,labelspacing=.2,prop={'size':7})
l.get_frame().set_alpha(.4)
fig.savefig(out)
print 'Included simulations:',', '.join(titlesIncluded)
if titlesSkipped: print 'Skipped simulations:',', '.join(titlesSkipped)
print 'Batch figure saved to file://%s'%os.path.abspath(out)
def triaxDone(S):
print 'Triaxial done at step',S.step
if S.pre.saveFmt:
out=S.pre.saveFmt.format(stage='done',S=S,**(dict(S.tags)))
print 'Saving to',out
S.save(out)
S.stop()
import woo.utils
(repName,figs)=woo.utils.htmlReport(S,S.pre.reportFmt,'Cylindrical triaxial test',afterHead='',figures=[(None,f) for f in S.plot.plot(noShow=True,subPlots=False)],svgEmbed=True,show=True)
woo.batch.writeResults(S,defaultDb='cylTriax.hdf5',series=S.plot.data,postHooks=[plotBatchResults],simulationName='cylTriax',report='file://'+repName)
|