yade-dev team mailing list archive
-
yade-dev team
-
Mailing list archive
-
Message #02326
[Branch ~yade-dev/yade/trunk] Rev 1819: 1. Move import functions from utils to yade.ymport (sorry, import is python keyword and cannot be...
------------------------------------------------------------
revno: 1819
committer: Václav Šmilauer <eudoxos@xxxxxxxx>
branch nick: trunk
timestamp: Tue 2009-11-24 20:12:10 +0100
message:
1. Move import functions from utils to yade.ymport (sorry, import is python keyword and cannot be used ;-) ). yade.utils contains proxy function that warn and call the implementation in ymport.
2. Added virtual book Material::stateTypeOk(State*).
2. MetaBody checks that material--state couples are OK at the first run (like initializers, but not listed among initializers...); throws on error. Added this to regression tests.
3. O.step() now calls MetaBody::moveToNextTimeStep() directly, instead of spawning worker for 1 iteration.
4. Adapt stl import test to yade.ymport (the facets look weird, though?)
added:
py/ymport.py
modified:
core/Material.hpp
core/MetaBody.cpp
core/MetaBody.hpp
core/NullGUI.cpp
core/Omega.cpp
core/SimulationFlow.cpp
examples/STLImporterTest.py
pkg/dem/meta/ConcretePM.hpp
py/SConscript
py/tests/omega.py
py/utils.py
py/yadeWrapper/yadeWrapper.cpp
scripts/test/regular-sphere-pack.py
--
lp:yade
https://code.launchpad.net/~yade-dev/yade/trunk
Your team Yade developers is subscribed to branch lp:yade.
To unsubscribe from this branch go to https://code.launchpad.net/~yade-dev/yade/trunk/+edit-subscription.
=== modified file 'core/Material.hpp'
--- core/Material.hpp 2009-11-24 17:03:29 +0000
+++ core/Material.hpp 2009-11-24 19:12:10 +0000
@@ -27,7 +27,14 @@
//! Function to return empty default-initialized instance of State that
// is supposed to go along with this Material. Don't override unless you need
// something else than basic State.
- virtual shared_ptr<State> newAssocState(){ return shared_ptr<State>(new State); }
+ virtual shared_ptr<State> newAssocState() const { return shared_ptr<State>(new State); }
+ /*! Function that returns true if given State instance is what this material expects.
+
+ Base Material class has no requirements, but the check would normally look like this:
+
+ return (bool)dynamic_cast<State*> state;
+ */
+ virtual bool stateTypeOk(State* state) const { return true; }
static const shared_ptr<Material> byId(int id, MetaBody* world=NULL);
static const shared_ptr<Material> byId(int id, shared_ptr<MetaBody> world) {return byId(id,world.get());}
=== modified file 'core/MetaBody.cpp'
--- core/MetaBody.cpp 2009-11-24 17:03:29 +0000
+++ core/MetaBody.cpp 2009-11-24 19:12:10 +0000
@@ -79,9 +79,9 @@
-void MetaBody::moveToNextTimeStep()
-{
+void MetaBody::moveToNextTimeStep(){
if(needsInitializers){
+ checkStateTypes();
FOREACH(shared_ptr<Engine> e, initializers){ if(e->isActivated(this)) e->action(this); }
bex.resize(bodies->size());
needsInitializers=false;
@@ -95,6 +95,8 @@
if(TimingInfo_enabled) {TimingInfo::delta now=TimingInfo::getNow(); e->timingInfo.nsec+=now-last; e->timingInfo.nExec+=1; last=now;}
}
}
+ currentIteration++;
+ simulationTime+=dt;
}
shared_ptr<Engine> MetaBody::engineByName(string s){
@@ -119,3 +121,12 @@
}
}
+void MetaBody::checkStateTypes(){
+ FOREACH(const shared_ptr<Body>& b, *bodies){
+ if(!b || !b->material) continue;
+ if(b->material && !b->state) throw std::runtime_error("Body #"+lexical_cast<string>(b->getId())+": has Body::material, but NULL Body::state.");
+ if(!b->material->stateTypeOk(b->state.get())){
+ throw std::runtime_error("Body #"+lexical_cast<string>(b->getId())+": Body::material type "+b->material->getClassName()+" doesn't correspond to Body::state type "+b->state->getClassName()+" (should be "+b->material->newAssocState()->getClassName()+" instead).");
+ }
+ }
+}
=== modified file 'core/MetaBody.hpp'
--- core/MetaBody.hpp 2009-11-23 12:32:14 +0000
+++ core/MetaBody.hpp 2009-11-24 19:12:10 +0000
@@ -33,6 +33,8 @@
vector<shared_ptr<Material> > materials;
//! Adds material to MetaBody::materials. It also sets id of the material accordingly and returns it.
int addMaterial(shared_ptr<Material> m){ materials.push_back(m); m->id=(int)materials.size()-1; return m->id; }
+ //! Checks that type of Body::state satisfies Material::stateTypeOk. Throws runtime_error if not. (Is called from BoundingVolumeMetaEngine the first time it runs)
+ void checkStateTypes();
BexContainer bex;
=== modified file 'core/NullGUI.cpp'
--- core/NullGUI.cpp 2009-05-04 13:31:07 +0000
+++ core/NullGUI.cpp 2009-11-24 19:12:10 +0000
@@ -168,8 +168,6 @@
while(1)
{
Omega::instance().getRootBody()->moveToNextTimeStep();
- Omega::instance().incrementCurrentIteration();
- Omega::instance().incrementSimulationTime();
if(Omega::instance().getCurrentIteration() % interval == 0 )
{
=== modified file 'core/Omega.cpp'
--- core/Omega.cpp 2009-08-22 15:05:20 +0000
+++ core/Omega.cpp 2009-11-24 19:12:10 +0000
@@ -52,12 +52,10 @@
const map<string,DynlibDescriptor>& Omega::getDynlibsDescriptor(){return dynlibs;}
-void Omega::incrementCurrentIteration(){ if(rootBody) rootBody->currentIteration++;}
long int Omega::getCurrentIteration(){ return (rootBody?rootBody->currentIteration:-1); }
void Omega::setCurrentIteration(long int i) { if(rootBody) rootBody->currentIteration=i; }
Real Omega::getSimulationTime() { return rootBody?rootBody->simulationTime:-1;};
-void Omega::incrementSimulationTime() { if(rootBody) rootBody->simulationTime+=getTimeStep();};
void Omega::setSimulationFileName(const string f){simulationFileName = f;}
string Omega::getSimulationFileName(){return simulationFileName;}
=== modified file 'core/SimulationFlow.cpp'
--- core/SimulationFlow.cpp 2008-07-21 12:12:23 +0000
+++ core/SimulationFlow.cpp 2009-11-24 19:12:10 +0000
@@ -16,8 +16,6 @@
if (OO.getRootBody()) // FIXME - would it contain the loop in the private variables, this check would be unnecessary
{
OO.getRootBody()->moveToNextTimeStep();
- OO.incrementCurrentIteration();
- OO.incrementSimulationTime();
if(OO.getRootBody()->stopAtIteration>0 && OO.getCurrentIteration()==OO.getRootBody()->stopAtIteration){
setTerminate(true);
}
=== modified file 'examples/STLImporterTest.py'
--- examples/STLImporterTest.py 2009-11-22 15:59:02 +0000
+++ examples/STLImporterTest.py 2009-11-24 19:12:10 +0000
@@ -10,8 +10,11 @@
## Import wall's geometry
params=utils.getViscoelasticFromSpheresInteraction(10e3,tc,en,es)
-imported = utils.import_stl_geometry('baraban.stl',frictionAngle=frictionAngle,materialClass="SimpleViscoelasticMat",**params) # **params sets kn, cn, ks, cs
-
+print params
+facetMat=O.materials.append(SimpleViscoelasticMat(frictionAngle=frictionAngle,**params)) # **params sets kn, cn, ks, cs
+sphereMat=O.materials.append(SimpleViscoelasticMat(density=Density,frictionAngle=frictionAngle,**params))
+from yade import ymport
+imported = ymport.stl('baraban.stl',material=facetMat)
## Spheres
sphereRadius = 0.2
nbSpheres = (10,10,10)
@@ -22,7 +25,7 @@
x = (i*2 - nbSpheres[0])*sphereRadius*1.1
y = j*sphereRadius*2.2
z = (k*2 - nbSpheres[2])*sphereRadius*1.1
- s=utils.sphere([x,y,z],sphereRadius,density=Density,frictionAngle=frictionAngle,materialClass="SimpleViscoelasticMat")
+ s=utils.sphere([x,y,z],sphereRadius,material=sphereMat)
p=utils.getViscoelasticFromSpheresInteraction(s.state['mass'],tc,en,es)
s.mat['kn'],s.mat['cn'],s.mat['ks'],s.mat['cs']=p['kn'],p['cn'],p['ks'],p['cs']
O.bodies.append(s)
=== modified file 'pkg/dem/meta/ConcretePM.hpp'
--- pkg/dem/meta/ConcretePM.hpp 2009-11-24 17:03:29 +0000
+++ pkg/dem/meta/ConcretePM.hpp 2009-11-24 19:12:10 +0000
@@ -85,7 +85,8 @@
class CpmMat: public GranularMat {
public:
CpmMat() { createIndex(); density=4800; };
- virtual shared_ptr<State> newAssocState(){ return shared_ptr<State>(new CpmState); }
+ virtual shared_ptr<State> newAssocState() const { return shared_ptr<State>(new CpmState); }
+ virtual bool stateTypeOk(State* s) const { return (bool)dynamic_cast<CpmState*>(s); }
REGISTER_ATTRIBUTES(GranularMat,);
REGISTER_CLASS_AND_BASE(CpmMat,GranularMat);
REGISTER_CLASS_INDEX(CpmMat,GranularMat);
=== modified file 'py/SConscript'
--- py/SConscript 2009-11-23 12:32:14 +0000
+++ py/SConscript 2009-11-24 19:12:10 +0000
@@ -22,6 +22,7 @@
]),
env.SharedLibrary('_packObb',['_packObb.cpp'],SHLIBPREFIX=''),
env.File('utils.py'),
+ env.File('ymport.py'),
env.File('eudoxos.py'),
env.File('plot.py'),
env.File('linterpolation.py'),
=== modified file 'py/tests/omega.py'
--- py/tests/omega.py 2009-11-23 12:32:14 +0000
+++ py/tests/omega.py 2009-11-24 19:12:10 +0000
@@ -18,6 +18,19 @@
class TestIO(unittest.TestCase): pass
class TestTags(unittest.TestCase): pass
+class TestMaterialStateAssociativity(unittest.TestCase):
+ def setUp(self): O.reset()
+ def testThrowsAtBadCombination(self):
+ "throws when body has material and state that don't work together."
+ b=Body()
+ b.mat=CpmMat()
+ b.state=State() #should be CpmState()
+ O.bodies.append(b)
+ self.assertRaises(RuntimeError,lambda: O.step()) # throws runtime_error
+ def testMaterialReturnsState(self):
+ "CpmMat returns CpmState when asked for newAssocState"
+ self.assert_(CpmMat().newAssocState().name=='CpmState')
+
class TestBodies(unittest.TestCase):
def setUp(self):
O.reset()
=== modified file 'py/utils.py'
--- py/utils.py 2009-11-24 17:03:29 +0000
+++ py/utils.py 2009-11-24 19:12:10 +0000
@@ -269,24 +269,6 @@
if b['isDynamic'] or not onlyDynamic: b.mold['diffuseColor']=color
-def spheresFromFile(filename,scale=1.,wenjieFormat=False,**kw):
- """Load sphere coordinates from file, create spheres, insert them to the simulation.
-
- filename is the file holding ASCII numbers (at least 4 colums that hold x_center, y_center, z_center, radius).
- All remaining arguments are passed the the yade.utils.sphere function that creates the bodies.
-
- wenjieFormat will skip all lines that have exactly 5 numbers and where the 4th one is exactly 1.0 -
- this was used by a fellow developer called Wenjie to mark box elements.
-
- Returns list of body ids that were inserted into simulation."""
- o=Omega()
- ret=[]
- for l in open(filename):
- ss=[float(i) for i in l.split()]
- if wenjieFormat and len(ss)==5 and ss[4]==1.0: continue
- id=o.bodies.append(sphere([scale*ss[0],scale*ss[1],scale*ss[2]],scale*ss[3],**kw))
- ret.append(id)
- return ret
def spheresToFile(filename,consider=lambda id: True):
"""Save sphere coordinates into ASCII file; the format of the line is: x y z r.
@@ -339,94 +321,9 @@
pylab.ylabel('Body count')
pylab.show()
-def import_stl_geometry(file, dynamic=False,wire=True,color=None,highlight=False,noBoundingVolume=False, **matKw):
- """ Import geometry from stl file, create facets and return list of their ids."""
- imp = STLImporter()
- imp.open(file)
- begin=len(O.bodies)
- imp.import_geometry(O.bodies)
- imported=range(begin,begin+imp.number_of_facets)
- for i in imported:
- b=O.bodies[i]
- b['isDynamic']=dynamic
- b.mold.postProcessAttributes(True)
- b.mold['diffuseColor']=color if color else randomColor()
- b.mold['wire']=wire
- b.mold['highlight']=highlight
- _commonBodySetup(b,0,Vector3(0,0,0),noBound=noBoundingVolume,**matKw)
- return imported
-
-
-def import_mesh_geometry(meshfile="file.mesh",**kw):
- """ Imports geometry from mesh file and creates facets.
- Remaining **kw arguments are passed to utils.facet;
- mesh files can be easily created with GMSH http://www.geuz.org/gmsh/
- Example added to scripts/test/regular-sphere-pack.py"""
- infile = open(meshfile,"r")
- lines = infile.readlines()
- infile.close()
-
- nodelistVector3=[]
- numNodes = int(lines[4].split()[0])
- for i in range(numNodes):
- nodelistVector3.append(Vector3(0.0,0.0,0.0))
- id = 0
- for line in lines[5:numNodes+5]:
- data = line.split()
- X = float(data[0])
- Y = float(data[1])
- Z = float(data[2])
- nodelistVector3[id] = Vector3(X,Y,Z)
- id += 1
- numTriangles = int(lines[numNodes+6].split()[0])
- triList = []
- for i in range(numTriangles):
- triList.append([0,0,0,0])
-
- tid = 0
- for line in lines[numNodes+7:numNodes+7+numTriangles]:
- data = line.split()
- id1 = int(data[0])-1
- id2 = int(data[1])-1
- id3 = int(data[2])-1
- triList[tid][0] = tid
- triList[tid][1] = id1
- triList[tid][2] = id2
- triList[tid][3] = id3
- tid += 1
- ret=[]
- for i in triList:
- a=nodelistVector3[i[1]]
- b=nodelistVector3[i[2]]
- c=nodelistVector3[i[3]]
- ret.append(facet((nodelistVector3[i[1]],nodelistVector3[i[2]],nodelistVector3[i[3]]),**kw))
- return ret
-
-def import_LSMGenGeo_geometry(fileName="file.geo",moveTo=[0.0,0.0,0.0],**kw):
- """ Imports geometry from LSMGenGeo .geo file and creates spheres.
- moveTo[X,Y,Z] parameter moves the specimen.
- Remaining **kw arguments are passed to utils.sphere;
-
- LSMGenGeo library allows to create pack of spheres
- with given [Rmin:Rmax] with null stress inside the specimen.
- Can be usefull for Mining Rock simulation.
-
- Example added to scripts/test/regular-sphere-pack.py
- Example of LSMGenGeo library using is added to genCylLSM.py
-
- http://www.access.edu.au/lsmgengeo_python_doc/current/pythonapi/html/GenGeo-module.html
- https://svn.esscc.uq.edu.au/svn/esys3/lsm/contrib/LSMGenGeo/"""
-
- infile = open(fileName,"r")
- lines = infile.readlines()
- infile.close()
-
- numSpheres = int(lines[6].split()[0])
- ret=[]
- for line in lines[7:numSpheres+7]:
- data = line.split()
- ret.append(sphere([moveTo[0]+float(data[0]),moveTo[1]+float(data[1]),moveTo[2]+float(data[2])],float(data[3]),**kw))
- return ret
+
+
+
def encodeVideoFromFrames(frameSpec,out,renameNotOverwrite=True,fps=24):
"""Create .ogg video from external image files.
@@ -600,3 +497,27 @@
If the last point's x coord is zero, it will not be duplicated."""
return list(half)+[(x,-y) for x,y in reversed(half[:-1] if half[-1][0]==0 else half)]
+#############################
+##### deprecated functions
+
+
+def _deprecatedUtilsFunction(old,new):
+ import warnings
+ warnings.warn('Function utils.%s is deprecated, use %s instead.'%(old,new),stacklevel=2,category='DeprecationWarning')
+
+def spheresFromFile(*args,**kw):
+ _deprecatedUtilsFunction(func.__name__,'yade.import.text')
+ import yade.ymport
+ return yade.ymport.text(*args,**kw)
+def import_stl_geometry(*args,**kw):
+ _deprecatedUtilsFunction(func.__name__,'yade.import.stl')
+ import yade.ymport
+ return yade.ymport.stl(*args,**kw)
+def import_mesh_geometry(*args,**kw):
+ _deprecatedUtilsFunction(func.__name__,'yade.import.gmsh')
+ import yade.ymport
+ return yade.ymport.stl(*args,**kw)
+def import_LSMGenGeo_geometry(*args,**kw):
+ _deprecatedUtilsFunction(func.__name__,'yade.import.gengeo')
+ import yade.ymport
+ return yade.ymport.gengeo(*args,**kw)
=== modified file 'py/yadeWrapper/yadeWrapper.cpp'
--- py/yadeWrapper/yadeWrapper.cpp 2009-11-24 17:03:29 +0000
+++ py/yadeWrapper/yadeWrapper.cpp 2009-11-24 19:12:10 +0000
@@ -376,7 +376,7 @@
if(doWait) wait();
}
void pause(){Py_BEGIN_ALLOW_THREADS; OMEGA.stopSimulationLoop(); Py_END_ALLOW_THREADS; LOG_DEBUG("PAUSE!");}
- void step() { LOG_DEBUG("STEP!"); run(1); wait(); }
+ void step() { if(OMEGA.isRunning()) throw runtime_error("Called O.step() while simulation is running."); OMEGA.getRootBody()->moveToNextTimeStep(); /* LOG_DEBUG("STEP!"); run(1); wait(); */ }
void wait(){ if(OMEGA.isRunning()){LOG_DEBUG("WAIT!");} else return; timespec t1,t2; t1.tv_sec=0; t1.tv_nsec=40000000; /* 40 ms */ Py_BEGIN_ALLOW_THREADS; while(OMEGA.isRunning()) nanosleep(&t1,&t2); Py_END_ALLOW_THREADS; }
void load(std::string fileName) {
=== added file 'py/ymport.py'
--- py/ymport.py 1970-01-01 00:00:00 +0000
+++ py/ymport.py 2009-11-24 19:12:10 +0000
@@ -0,0 +1,118 @@
+"""
+Import geometry from various formats ('import' is python keyword, hence the name 'ymport').
+"""
+
+from yade.wrapper import *
+from miniWm3Wrap import *
+from yade import utils
+
+
+def ascii(filename,scale=1.,wenjieFormat=False,**kw):
+ """Load sphere coordinates from file, create spheres, insert them to the simulation.
+
+ filename is the file holding ASCII numbers (at least 4 colums that hold x_center, y_center, z_center, radius).
+ All remaining arguments are passed the the yade.utils.sphere function that creates the bodies.
+
+ wenjieFormat will skip all lines that have exactly 5 numbers and where the 4th one is exactly 1.0 -
+ this was used by a fellow developer called Wenjie to mark box elements.
+
+ Returns list of body ids that were inserted into simulation."""
+ from yade.utils import sphere
+ o=Omega()
+ ret=[]
+ for l in open(filename):
+ ss=[float(i) for i in l.split()]
+ if wenjieFormat and len(ss)==5 and ss[4]==1.0: continue
+ id=o.bodies.append(sphere([scale*ss[0],scale*ss[1],scale*ss[2]],scale*ss[3],**kw))
+ ret.append(id)
+ return ret
+
+def stl(file, dynamic=False,wire=True,color=None,highlight=False,noBoundingVolume=False,material=0):
+ """ Import geometry from stl file, create facets and return list of their ids."""
+ imp = STLImporter()
+ imp.open(file)
+ begin=len(O.bodies)
+ imp.import_geometry(O.bodies)
+ imported=range(begin,begin+imp.number_of_facets)
+ for i in imported:
+ b=O.bodies[i]
+ b['isDynamic']=dynamic
+ b.mold.postProcessAttributes(True)
+ b.mold['diffuseColor']=color if color else utils.randomColor()
+ b.mold['wire']=wire
+ b.mold['highlight']=highlight
+ utils._commonBodySetup(b,0,Vector3(0,0,0),noBound=noBoundingVolume,material=material)
+ return imported
+
+def gmsh(meshfile="file.mesh",**kw):
+ """ Imports geometry from mesh file and creates facets.
+ Remaining **kw arguments are passed to utils.facet;
+ mesh files can be easily created with GMSH http://www.geuz.org/gmsh/
+ Example added to scripts/test/regular-sphere-pack.py"""
+ infile = open(meshfile,"r")
+ lines = infile.readlines()
+ infile.close()
+
+ nodelistVector3=[]
+ numNodes = int(lines[4].split()[0])
+ for i in range(numNodes):
+ nodelistVector3.append(Vector3(0.0,0.0,0.0))
+ id = 0
+ for line in lines[5:numNodes+5]:
+ data = line.split()
+ X = float(data[0])
+ Y = float(data[1])
+ Z = float(data[2])
+ nodelistVector3[id] = Vector3(X,Y,Z)
+ id += 1
+ numTriangles = int(lines[numNodes+6].split()[0])
+ triList = []
+ for i in range(numTriangles):
+ triList.append([0,0,0,0])
+
+ tid = 0
+ for line in lines[numNodes+7:numNodes+7+numTriangles]:
+ data = line.split()
+ id1 = int(data[0])-1
+ id2 = int(data[1])-1
+ id3 = int(data[2])-1
+ triList[tid][0] = tid
+ triList[tid][1] = id1
+ triList[tid][2] = id2
+ triList[tid][3] = id3
+ tid += 1
+ ret=[]
+ for i in triList:
+ a=nodelistVector3[i[1]]
+ b=nodelistVector3[i[2]]
+ c=nodelistVector3[i[3]]
+ ret.append(utils.facet((nodelistVector3[i[1]],nodelistVector3[i[2]],nodelistVector3[i[3]]),**kw))
+ return ret
+
+def gengeo(fileName="file.geo",moveTo=[0.0,0.0,0.0],**kw):
+ """ Imports geometry from LSMGenGeo .geo file and creates spheres.
+ moveTo[X,Y,Z] parameter moves the specimen.
+ Remaining **kw arguments are passed to utils.sphere;
+
+ LSMGenGeo library allows to create pack of spheres
+ with given [Rmin:Rmax] with null stress inside the specimen.
+ Can be usefull for Mining Rock simulation.
+
+ Example added to scripts/test/regular-sphere-pack.py
+ Example of LSMGenGeo library using is added to genCylLSM.py
+
+ http://www.access.edu.au/lsmgengeo_python_doc/current/pythonapi/html/GenGeo-module.html
+ https://svn.esscc.uq.edu.au/svn/esys3/lsm/contrib/LSMGenGeo/"""
+ from yade.utils import sphere
+
+ infile = open(fileName,"r")
+ lines = infile.readlines()
+ infile.close()
+
+ numSpheres = int(lines[6].split()[0])
+ ret=[]
+ for line in lines[7:numSpheres+7]:
+ data = line.split()
+ ret.append(utils.sphere([moveTo[0]+float(data[0]),moveTo[1]+float(data[1]),moveTo[2]+float(data[2])],float(data[3]),**kw))
+ return ret
+
=== modified file 'scripts/test/regular-sphere-pack.py'
--- scripts/test/regular-sphere-pack.py 2009-11-24 17:03:29 +0000
+++ scripts/test/regular-sphere-pack.py 2009-11-24 19:12:10 +0000
@@ -1,4 +1,4 @@
-from yade import pack
+from yade import pack,ymport
""" This script demonstrates how to use 2 components of creating packings:
@@ -58,10 +58,10 @@
"""Import regular-sphere-pack.mesh into the YADE simulation"""
-O.bodies.append(utils.import_mesh_geometry('regular-sphere-pack.mesh',**kwMeshes))#generates facets from the mesh file
+O.bodies.append(ymport.gmsh('regular-sphere-pack.mesh',**kwMeshes))#generates facets from the mesh file
"""Import regular-sphere-pack-LSMGegGeo.geo into the YADE simulation"""
-O.bodies.append(utils.import_LSMGenGeo_geometry('regular-sphere-pack-LSMGegGeo.geo',moveTo=[-7.0,-7.0,-5.9],color=(1,0,1),**kw))
+O.bodies.append(ymport.gengeo('regular-sphere-pack-LSMGegGeo.geo',moveTo=[-7.0,-7.0,-5.9],color=(1,0,1),**kw))
try: