From d61a17cf10890522ab0510a952e20bdac72f7487 Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Fri, 29 Jan 2016 00:42:53 -0500 Subject: [PATCH 01/83] Unit tests for data types --- .gitignore | 4 + test/MMSP.unitTestSuite.hpp | 238 ++++++++++++++++++++++++++++++++++++ test/Makefile | 7 +- 3 files changed, 248 insertions(+), 1 deletion(-) create mode 100644 test/MMSP.unitTestSuite.hpp diff --git a/.gitignore b/.gitignore index 79c5c4f..60fb49f 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,10 @@ utility/TKmmsp2vti utility/vox2MC utility/wrongendian +# unit testing +test/MMSP.unitTestSuite.cpp +test/unitTestSuite + # Compiled Object files *.slo *.lo diff --git a/test/MMSP.unitTestSuite.hpp b/test/MMSP.unitTestSuite.hpp new file mode 100644 index 0000000..9a3d61d --- /dev/null +++ b/test/MMSP.unitTestSuite.hpp @@ -0,0 +1,238 @@ +/* MMSP.unitTestSuite.hpp +** Use CxxTest to build unit tests for MMSP classes +** +** +*/ +#include"MMSP.hpp" +#include + +class scalarTestSuite : public CxxTest::TestSuite +{ +private: + MMSP::scalar sc; + MMSP::scalar ss; + MMSP::scalar si; + MMSP::scalar sl; + MMSP::scalar sf; + MMSP::scalar sd; + +public: + scalarTestSuite() { + sc = 2; + ss = 2; + si = 2; + sl = 2; + sf = 2.0; + sd = 2.0; + } + void testSize(void){ + TS_ASSERT_EQUALS(sc.buffer_size(),sizeof(char)); + TS_ASSERT_EQUALS(ss.buffer_size(),sizeof(short)); + TS_ASSERT_EQUALS(si.buffer_size(),sizeof(int)); + TS_ASSERT_EQUALS(sl.buffer_size(),sizeof(long)); + TS_ASSERT_EQUALS(sf.buffer_size(),sizeof(float)); + TS_ASSERT_EQUALS(sd.buffer_size(),sizeof(double)); + } + void testAddition(void){ + TS_ASSERT_EQUALS(sc+sc,char(4)); + TS_ASSERT_EQUALS(ss+ss,short(4)); + TS_ASSERT_EQUALS(si+si,int(4)); + TS_ASSERT_EQUALS(sl+sl,long(4)); + TS_ASSERT_EQUALS(sf+sf,float(4)); + TS_ASSERT_EQUALS(sd+sd,double(4)); + } + void testSquares(void){ + TS_ASSERT_EQUALS(sc*sc,char(4)); + TS_ASSERT_EQUALS(ss*ss,short(4)); + TS_ASSERT_EQUALS(si*si,int(4)); + TS_ASSERT_EQUALS(sl*sl,long(4)); + TS_ASSERT_EQUALS(sf*sf,float(4)); + TS_ASSERT_EQUALS(sd*sd,double(4)); + } + void testMultiplication(void){ + TS_ASSERT_EQUALS(2*sc,char(4)); + TS_ASSERT_EQUALS(2*ss,short(4)); + TS_ASSERT_EQUALS(2*si,int(4)); + TS_ASSERT_EQUALS(2*sl,long(4)); + TS_ASSERT_EQUALS(2*sf,float(4)); + TS_ASSERT_EQUALS(2*sd,double(4)); + } +}; + +class vectorTestSuite : public CxxTest::TestSuite +{ +private: + MMSP::vector vc; + MMSP::vector vs; + MMSP::vector vi; + MMSP::vector vl; + MMSP::vector vf; + MMSP::vector vd; + +public: + vectorTestSuite() { + MMSP::vector temp(3,2); + vc = temp; + vs = temp; + vi = temp; + vl = temp; + vf = temp; + vd = temp; + } + void testValue(void){ + for (int i=0; i<3; i++) { + TS_ASSERT_EQUALS(vc[i],char(2)); + TS_ASSERT_EQUALS(vs[i],short(2)); + TS_ASSERT_EQUALS(vi[i],int(2)); + TS_ASSERT_EQUALS(vl[i],long(2)); + TS_ASSERT_EQUALS(vf[i],float(2)); + TS_ASSERT_EQUALS(vd[i],double(2)); + } + } + void testSize(void){ + TS_ASSERT_EQUALS(vc.length(),3); + TS_ASSERT_EQUALS(vs.length(),3); + TS_ASSERT_EQUALS(vi.length(),3); + TS_ASSERT_EQUALS(vl.length(),3); + TS_ASSERT_EQUALS(vf.length(),3); + TS_ASSERT_EQUALS(vd.length(),3); + + TS_ASSERT_EQUALS(vc.buffer_size(),sizeof(int)+3*sizeof(char)); + TS_ASSERT_EQUALS(vs.buffer_size(),sizeof(int)+3*sizeof(short)); + TS_ASSERT_EQUALS(vi.buffer_size(),4*sizeof(int)); + TS_ASSERT_EQUALS(vl.buffer_size(),sizeof(int)+3*sizeof(long)); + TS_ASSERT_EQUALS(vf.buffer_size(),sizeof(int)+3*sizeof(float)); + TS_ASSERT_EQUALS(vd.buffer_size(),sizeof(int)+3*sizeof(double)); + } + void testAddition(void){ + MMSP::vector temp(3,4); + //TS_ASSERT_EQUALS(vc+vc,temp); + //TS_ASSERT_EQUALS(vs+vs,temp); + TS_ASSERT_EQUALS(vi+vi,temp); + /*TS_ASSERT_EQUALS(vl+vl,temp); + TS_ASSERT_EQUALS(vf+vf,temp); + TS_ASSERT_EQUALS(vd+vd,temp); + */ + } + void testSquares(void){ + MMSP::vector temp(3,4); + TS_ASSERT_EQUALS(vc*vc,12); + TS_ASSERT_EQUALS(vs*vs,12); + TS_ASSERT_EQUALS(vi*vi,12); + TS_ASSERT_EQUALS(vl*vl,12); + TS_ASSERT_EQUALS(vf*vf,12); + TS_ASSERT_EQUALS(vd*vd,12); + } + void testMultiplication(void){ + MMSP::vector temp(3,4); + //TS_ASSERT_EQUALS(2*vc,temp); + TS_ASSERT_EQUALS(2*vi,temp); + /*TS_ASSERT_EQUALS(2*vl,temp); + TS_ASSERT_EQUALS(2*vf,temp); + TS_ASSERT_EQUALS(2*vd,temp); + */ + } +}; + +class sparseTestSuite : public CxxTest::TestSuite +{ +private: + MMSP::sparse svc; + MMSP::sparse svs; + MMSP::sparse svi; + MMSP::sparse svl; + MMSP::sparse svf; + MMSP::sparse svd; + +public: + sparseTestSuite() { + MMSP::sparse temp; + for (int i=0; i<3; i++) + temp.set(2*i) = 2; + svc = temp; + svs = temp; + svi = temp; + svl = temp; + svf = temp; + svd = temp; + } + void testValue(void){ + for (int i=0; i<6; i+=2) { + TS_ASSERT_EQUALS(svc[i],char(2)); + TS_ASSERT_EQUALS(svs[i],short(2)); + TS_ASSERT_EQUALS(svi[i],int(2)); + TS_ASSERT_EQUALS(svl[i],long(2)); + TS_ASSERT_EQUALS(svf[i],float(2)); + TS_ASSERT_EQUALS(svd[i],double(2)); + } + for (int i=1; i<5; i+=2) { + TS_ASSERT_EQUALS(svc[i],char(0)); + TS_ASSERT_EQUALS(svs[i],short(0)); + TS_ASSERT_EQUALS(svi[i],int(0)); + TS_ASSERT_EQUALS(svl[i],long(0)); + TS_ASSERT_EQUALS(svf[i],float(0)); + TS_ASSERT_EQUALS(svd[i],double(0)); + } + } + void testSize(void){ + TS_ASSERT_EQUALS(svc.length(),3); + TS_ASSERT_EQUALS(svs.length(),3); + TS_ASSERT_EQUALS(svi.length(),3); + TS_ASSERT_EQUALS(svl.length(),3); + TS_ASSERT_EQUALS(svf.length(),3); + TS_ASSERT_EQUALS(svd.length(),3); + + //TS_ASSERT_EQUALS(svc.buffer_size(),sizeof(int)+3*sizeof(int)+3*sizeof(char)); + TS_ASSERT_EQUALS(sizeof(int)+sizeof(int),sizeof(MMSP::item)); // that is unexpected + TS_ASSERT_EQUALS(svc.buffer_size(),sizeof(int)+3*sizeof(int)+3*sizeof(char)+3*(sizeof(int)-sizeof(char))); + TS_ASSERT_EQUALS(svs.buffer_size(),sizeof(int)+3*sizeof(int)+3*sizeof(short)+3*(sizeof(int)-sizeof(short))); + TS_ASSERT_EQUALS(svi.buffer_size(),sizeof(int)+3*sizeof(int)+3*sizeof(int)); + TS_ASSERT_EQUALS(svl.buffer_size(),sizeof(int)+3*sizeof(int)+3*sizeof(long)+3*(sizeof(long)-sizeof(int))); + TS_ASSERT_EQUALS(svf.buffer_size(),sizeof(int)+3*sizeof(int)+3*sizeof(float)); + TS_ASSERT_EQUALS(svd.buffer_size(),sizeof(int)+3*sizeof(int)+3*sizeof(double)+3*(sizeof(double)-sizeof(int))); + // More compactly: + TS_ASSERT_EQUALS(svc.buffer_size(),sizeof(int)+3*sizeof(int)+3*sizeof(int)); + TS_ASSERT_EQUALS(svs.buffer_size(),sizeof(int)+3*sizeof(int)+3*sizeof(int)); + TS_ASSERT_EQUALS(svi.buffer_size(),sizeof(int)+3*sizeof(int)+3*sizeof(int)); + TS_ASSERT_EQUALS(svl.buffer_size(),sizeof(int)+3*sizeof(long)+3*sizeof(long)); + TS_ASSERT_EQUALS(svf.buffer_size(),sizeof(int)+3*sizeof(int)+3*sizeof(float)); + TS_ASSERT_EQUALS(svd.buffer_size(),sizeof(int)+3*sizeof(double)+3*sizeof(double)); + } + void testAddition(void){ + MMSP::sparse temp; + for (int i=0; i<3; i++) + temp.set(2*i) = 4; + //TS_ASSERT_EQUALS(svc+svc,temp); + //TS_ASSERT_EQUALS(svs+svs,temp); + TS_ASSERT_EQUALS(svi+svi,temp); + /*TS_ASSERT_EQUALS(svl+svl,temp); + TS_ASSERT_EQUALS(svf+svf,temp); + TS_ASSERT_EQUALS(svd+svd,temp); + */ + } + void testSquares(void){ + MMSP::sparse temp; + for (int i=0; i<3; i++) + temp.set(2*i) = 4; + //TS_ASSERT_EQUALS(svc*svc,temp); + //TS_ASSERT_EQUALS(svs*svs,temp); + //TS_ASSERT_EQUALS(svi*svi,temp); + /*TS_ASSERT_EQUALS(svl*svl,temp); + TS_ASSERT_EQUALS(svf*svf,temp); + TS_ASSERT_EQUALS(svd*svd,temp); + */ + } + void testMultiplication(void){ + MMSP::sparse temp; + for (int i=0; i<3; i++) + temp.set(2*i) = 4; + //TS_ASSERT_EQUALS(2*svc,temp); + //TS_ASSERT_EQUALS(2*svs,temp); + TS_ASSERT_EQUALS(2*svi,temp); + /*TS_ASSERT_EQUALS(2*svl,temp); + TS_ASSERT_EQUALS(2*svf,temp); + TS_ASSERT_EQUALS(2*svd,temp); + */ + } +}; + diff --git a/test/Makefile b/test/Makefile index 15d5dd4..0fe18a0 100644 --- a/test/Makefile +++ b/test/Makefile @@ -26,5 +26,10 @@ test: test.cpp $(core) parallel: test.cpp $(core) $(pcompiler) $(pflags) $< -o $@ +unitTestSuite: MMSP.unitTestSuite.hpp + cxxtestgen --error-printer -o MMSP.unitTestSuite.cpp $< && \ + $(compiler) -std=c++11 -O2 -I$(incdir) -o $@ MMSP.unitTestSuite.cpp && \ + ./$@ + clean: - rm -f test parallel + rm -f test parallel MMSP.unitTestSuite.cpp unitTestSuite From b2a819cc9379340cfbdc77a8cc917943adab3e9c Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Fri, 29 Jan 2016 16:55:13 -0500 Subject: [PATCH 02/83] Vector bounds checking, purge invalid sparse tests --- include/MMSP.utility.hpp | 3 +- include/MMSP.vector.hpp | 5 +++ test/MMSP.unitTestSuite.hpp | 77 +++++++++++++++---------------------- test/Makefile | 11 +++--- 4 files changed, 45 insertions(+), 51 deletions(-) diff --git a/include/MMSP.utility.hpp b/include/MMSP.utility.hpp index 996e2fd..cc5212b 100644 --- a/include/MMSP.utility.hpp +++ b/include/MMSP.utility.hpp @@ -329,7 +329,8 @@ template T global(T& value, const char* operation) { Call once inside the update function (or equivalent). for (int step=0; step namespace MMSP { @@ -46,9 +47,13 @@ template class vector { // data access operators T& operator[](int i) { + if (i>=size) + throw std::out_of_range("index exceeds vector size"); return data[i]; } const T& operator[](int i) const { + if (i>=size) + throw std::out_of_range("index exceeds vector size"); return data[i]; } diff --git a/test/MMSP.unitTestSuite.hpp b/test/MMSP.unitTestSuite.hpp index 9a3d61d..5247404 100644 --- a/test/MMSP.unitTestSuite.hpp +++ b/test/MMSP.unitTestSuite.hpp @@ -1,7 +1,6 @@ /* MMSP.unitTestSuite.hpp ** Use CxxTest to build unit tests for MMSP classes -** -** +** http://cxxtest.com */ #include"MMSP.hpp" #include @@ -103,18 +102,27 @@ class vectorTestSuite : public CxxTest::TestSuite TS_ASSERT_EQUALS(vl.buffer_size(),sizeof(int)+3*sizeof(long)); TS_ASSERT_EQUALS(vf.buffer_size(),sizeof(int)+3*sizeof(float)); TS_ASSERT_EQUALS(vd.buffer_size(),sizeof(int)+3*sizeof(double)); + + TS_ASSERT_THROWS_ANYTHING(vc[4]); + TS_ASSERT_THROWS_ANYTHING(vs[4]); + TS_ASSERT_THROWS_ANYTHING(vi[4]); + TS_ASSERT_THROWS_ANYTHING(vl[4]); + TS_ASSERT_THROWS_ANYTHING(vf[4]); + TS_ASSERT_THROWS_ANYTHING(vd[4]); } void testAddition(void){ MMSP::vector temp(3,4); - //TS_ASSERT_EQUALS(vc+vc,temp); - //TS_ASSERT_EQUALS(vs+vs,temp); - TS_ASSERT_EQUALS(vi+vi,temp); - /*TS_ASSERT_EQUALS(vl+vl,temp); - TS_ASSERT_EQUALS(vf+vf,temp); - TS_ASSERT_EQUALS(vd+vd,temp); - */ + for (int i=0; i<3; i++) { + TS_ASSERT_EQUALS((vc+vc)[i],temp[i]); + TS_ASSERT_EQUALS((vs+vs)[i],temp[i]); + TS_ASSERT_EQUALS((vi+vi)[i],temp[i]); + TS_ASSERT_EQUALS((vl+vl)[i],temp[i]); + TS_ASSERT_EQUALS((vf+vf)[i],temp[i]); + TS_ASSERT_EQUALS((vd+vd)[i],temp[i]); + } } void testSquares(void){ + // v*v is the inner (dot) product MMSP::vector temp(3,4); TS_ASSERT_EQUALS(vc*vc,12); TS_ASSERT_EQUALS(vs*vs,12); @@ -125,12 +133,13 @@ class vectorTestSuite : public CxxTest::TestSuite } void testMultiplication(void){ MMSP::vector temp(3,4); - //TS_ASSERT_EQUALS(2*vc,temp); - TS_ASSERT_EQUALS(2*vi,temp); - /*TS_ASSERT_EQUALS(2*vl,temp); - TS_ASSERT_EQUALS(2*vf,temp); - TS_ASSERT_EQUALS(2*vd,temp); - */ + for (int i=0; i<3; i++) { + TS_ASSERT_EQUALS((2*vc)[i],temp[i]); + TS_ASSERT_EQUALS((2*vi)[i],temp[i]); + TS_ASSERT_EQUALS((2*vl)[i],temp[i]); + TS_ASSERT_EQUALS((2*vf)[i],temp[i]); + TS_ASSERT_EQUALS((2*vd)[i],temp[i]); + } } }; @@ -182,7 +191,7 @@ class sparseTestSuite : public CxxTest::TestSuite TS_ASSERT_EQUALS(svf.length(),3); TS_ASSERT_EQUALS(svd.length(),3); - //TS_ASSERT_EQUALS(svc.buffer_size(),sizeof(int)+3*sizeof(int)+3*sizeof(char)); + TS_ASSERT_DIFFERS(sizeof(int)+sizeof(char),sizeof(MMSP::item)); // that is unexpected TS_ASSERT_EQUALS(sizeof(int)+sizeof(int),sizeof(MMSP::item)); // that is unexpected TS_ASSERT_EQUALS(svc.buffer_size(),sizeof(int)+3*sizeof(int)+3*sizeof(char)+3*(sizeof(int)-sizeof(char))); TS_ASSERT_EQUALS(svs.buffer_size(),sizeof(int)+3*sizeof(int)+3*sizeof(short)+3*(sizeof(int)-sizeof(short))); @@ -202,37 +211,15 @@ class sparseTestSuite : public CxxTest::TestSuite MMSP::sparse temp; for (int i=0; i<3; i++) temp.set(2*i) = 4; - //TS_ASSERT_EQUALS(svc+svc,temp); - //TS_ASSERT_EQUALS(svs+svs,temp); TS_ASSERT_EQUALS(svi+svi,temp); - /*TS_ASSERT_EQUALS(svl+svl,temp); - TS_ASSERT_EQUALS(svf+svf,temp); - TS_ASSERT_EQUALS(svd+svd,temp); - */ - } - void testSquares(void){ - MMSP::sparse temp; - for (int i=0; i<3; i++) - temp.set(2*i) = 4; - //TS_ASSERT_EQUALS(svc*svc,temp); - //TS_ASSERT_EQUALS(svs*svs,temp); - //TS_ASSERT_EQUALS(svi*svi,temp); - /*TS_ASSERT_EQUALS(svl*svl,temp); - TS_ASSERT_EQUALS(svf*svf,temp); - TS_ASSERT_EQUALS(svd*svd,temp); - */ } + // Note: Multiplying two sparse vectors is undefined by design. void testMultiplication(void){ - MMSP::sparse temp; - for (int i=0; i<3; i++) - temp.set(2*i) = 4; - //TS_ASSERT_EQUALS(2*svc,temp); - //TS_ASSERT_EQUALS(2*svs,temp); - TS_ASSERT_EQUALS(2*svi,temp); - /*TS_ASSERT_EQUALS(2*svl,temp); - TS_ASSERT_EQUALS(2*svf,temp); - TS_ASSERT_EQUALS(2*svd,temp); - */ + TS_ASSERT_EQUALS((2*svc)[0],4); + TS_ASSERT_EQUALS((2*svs)[0],4); + TS_ASSERT_EQUALS((2*svi)[0],4); + TS_ASSERT_EQUALS((2*svl)[0],4); + TS_ASSERT_EQUALS((2*svf)[0],4); + TS_ASSERT_EQUALS((2*svd)[0],4); } }; - diff --git a/test/Makefile b/test/Makefile index 0fe18a0..ac62925 100644 --- a/test/Makefile +++ b/test/Makefile @@ -7,9 +7,9 @@ incdir = ../include # compilers/flags compiler = g++ -flags = -O3 -I $(incdir) +flags = -O0 -I $(incdir) pcompiler = mpic++ -pflags = -O3 -I $(incdir) -include mpi.h +pflags = -O0 -I $(incdir) -include mpi.h # dependencies core = $(incdir)/MMSP.hpp \ @@ -28,8 +28,9 @@ parallel: test.cpp $(core) unitTestSuite: MMSP.unitTestSuite.hpp cxxtestgen --error-printer -o MMSP.unitTestSuite.cpp $< && \ - $(compiler) -std=c++11 -O2 -I$(incdir) -o $@ MMSP.unitTestSuite.cpp && \ - ./$@ + $(compiler) $(flags) --coverage -fprofile-arcs -ftest-coverage -fPIC -o $@ MMSP.unitTestSuite.cpp $(core) && \ + ./$@ && \ + gcovr -r . --html --html-details -o unitTestSuite.html clean: - rm -f test parallel MMSP.unitTestSuite.cpp unitTestSuite + rm -f test parallel MMSP.unitTestSuite.cpp unitTestSuite *.html *.gcno *.gcda From 05b90276e6cc3947256178c24e32e14db6f50376 Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Fri, 29 Jan 2016 17:01:41 -0500 Subject: [PATCH 03/83] Purge gcovr hooks, start on grid tests --- test/MMSP.unitTestSuite.hpp | 101 ++++++++++++++++++++++++++---------- test/Makefile | 7 ++- 2 files changed, 78 insertions(+), 30 deletions(-) diff --git a/test/MMSP.unitTestSuite.hpp b/test/MMSP.unitTestSuite.hpp index 5247404..24e4352 100644 --- a/test/MMSP.unitTestSuite.hpp +++ b/test/MMSP.unitTestSuite.hpp @@ -16,7 +16,7 @@ class scalarTestSuite : public CxxTest::TestSuite MMSP::scalar sd; public: - scalarTestSuite() { + void setUp() { sc = 2; ss = 2; si = 2; @@ -24,7 +24,7 @@ class scalarTestSuite : public CxxTest::TestSuite sf = 2.0; sd = 2.0; } - void testSize(void){ + void testSize(void) { TS_ASSERT_EQUALS(sc.buffer_size(),sizeof(char)); TS_ASSERT_EQUALS(ss.buffer_size(),sizeof(short)); TS_ASSERT_EQUALS(si.buffer_size(),sizeof(int)); @@ -32,7 +32,7 @@ class scalarTestSuite : public CxxTest::TestSuite TS_ASSERT_EQUALS(sf.buffer_size(),sizeof(float)); TS_ASSERT_EQUALS(sd.buffer_size(),sizeof(double)); } - void testAddition(void){ + void testAddition(void) { TS_ASSERT_EQUALS(sc+sc,char(4)); TS_ASSERT_EQUALS(ss+ss,short(4)); TS_ASSERT_EQUALS(si+si,int(4)); @@ -40,7 +40,7 @@ class scalarTestSuite : public CxxTest::TestSuite TS_ASSERT_EQUALS(sf+sf,float(4)); TS_ASSERT_EQUALS(sd+sd,double(4)); } - void testSquares(void){ + void testSquares(void) { TS_ASSERT_EQUALS(sc*sc,char(4)); TS_ASSERT_EQUALS(ss*ss,short(4)); TS_ASSERT_EQUALS(si*si,int(4)); @@ -48,7 +48,7 @@ class scalarTestSuite : public CxxTest::TestSuite TS_ASSERT_EQUALS(sf*sf,float(4)); TS_ASSERT_EQUALS(sd*sd,double(4)); } - void testMultiplication(void){ + void testMultiplication(void) { TS_ASSERT_EQUALS(2*sc,char(4)); TS_ASSERT_EQUALS(2*ss,short(4)); TS_ASSERT_EQUALS(2*si,int(4)); @@ -69,7 +69,7 @@ class vectorTestSuite : public CxxTest::TestSuite MMSP::vector vd; public: - vectorTestSuite() { + void setUp() { MMSP::vector temp(3,2); vc = temp; vs = temp; @@ -78,7 +78,7 @@ class vectorTestSuite : public CxxTest::TestSuite vf = temp; vd = temp; } - void testValue(void){ + void testValue(void) { for (int i=0; i<3; i++) { TS_ASSERT_EQUALS(vc[i],char(2)); TS_ASSERT_EQUALS(vs[i],short(2)); @@ -88,7 +88,7 @@ class vectorTestSuite : public CxxTest::TestSuite TS_ASSERT_EQUALS(vd[i],double(2)); } } - void testSize(void){ + void testSize(void) { TS_ASSERT_EQUALS(vc.length(),3); TS_ASSERT_EQUALS(vs.length(),3); TS_ASSERT_EQUALS(vi.length(),3); @@ -110,7 +110,7 @@ class vectorTestSuite : public CxxTest::TestSuite TS_ASSERT_THROWS_ANYTHING(vf[4]); TS_ASSERT_THROWS_ANYTHING(vd[4]); } - void testAddition(void){ + void testAddition(void) { MMSP::vector temp(3,4); for (int i=0; i<3; i++) { TS_ASSERT_EQUALS((vc+vc)[i],temp[i]); @@ -121,7 +121,7 @@ class vectorTestSuite : public CxxTest::TestSuite TS_ASSERT_EQUALS((vd+vd)[i],temp[i]); } } - void testSquares(void){ + void testSquares(void) { // v*v is the inner (dot) product MMSP::vector temp(3,4); TS_ASSERT_EQUALS(vc*vc,12); @@ -131,7 +131,7 @@ class vectorTestSuite : public CxxTest::TestSuite TS_ASSERT_EQUALS(vf*vf,12); TS_ASSERT_EQUALS(vd*vd,12); } - void testMultiplication(void){ + void testMultiplication(void) { MMSP::vector temp(3,4); for (int i=0; i<3; i++) { TS_ASSERT_EQUALS((2*vc)[i],temp[i]); @@ -152,20 +152,23 @@ class sparseTestSuite : public CxxTest::TestSuite MMSP::sparse svl; MMSP::sparse svf; MMSP::sparse svd; - -public: - sparseTestSuite() { - MMSP::sparse temp; + template void sparseInit(MMSP::sparse& s) { + MMSP::sparse temp; for (int i=0; i<3; i++) temp.set(2*i) = 2; - svc = temp; - svs = temp; - svi = temp; - svl = temp; - svf = temp; - svd = temp; - } - void testValue(void){ + s = temp; + } + +public: + void setUp() { + sparseInit(svc); + sparseInit(svs); + sparseInit(svi); + sparseInit(svl); + sparseInit(svf); + sparseInit(svd); + } + void testValue(void) { for (int i=0; i<6; i+=2) { TS_ASSERT_EQUALS(svc[i],char(2)); TS_ASSERT_EQUALS(svs[i],short(2)); @@ -183,7 +186,7 @@ class sparseTestSuite : public CxxTest::TestSuite TS_ASSERT_EQUALS(svd[i],double(0)); } } - void testSize(void){ + void testSize(void) { TS_ASSERT_EQUALS(svc.length(),3); TS_ASSERT_EQUALS(svs.length(),3); TS_ASSERT_EQUALS(svi.length(),3); @@ -207,14 +210,14 @@ class sparseTestSuite : public CxxTest::TestSuite TS_ASSERT_EQUALS(svf.buffer_size(),sizeof(int)+3*sizeof(int)+3*sizeof(float)); TS_ASSERT_EQUALS(svd.buffer_size(),sizeof(int)+3*sizeof(double)+3*sizeof(double)); } - void testAddition(void){ + void testAddition(void) { MMSP::sparse temp; for (int i=0; i<3; i++) temp.set(2*i) = 4; TS_ASSERT_EQUALS(svi+svi,temp); } // Note: Multiplying two sparse vectors is undefined by design. - void testMultiplication(void){ + void testMultiplication(void) { TS_ASSERT_EQUALS((2*svc)[0],4); TS_ASSERT_EQUALS((2*svs)[0],4); TS_ASSERT_EQUALS((2*svi)[0],4); @@ -223,3 +226,49 @@ class sparseTestSuite : public CxxTest::TestSuite TS_ASSERT_EQUALS((2*svd)[0],4); } }; + +class gridTestSuite : public CxxTest::TestSuite +{ +private: + +public: + void setUp(int argc, char* argv[]) { + MMSP::Init(argc, argv); + } + void tearDown() { + MMSP::Finalize(); + } + void testSize1D() { + MMSP::grid<1,MMSP::scalar > g1c(0, 0, 64); + MMSP::grid<1,MMSP::scalar > g1s(0, 0, 64); + MMSP::grid<1,MMSP::scalar > g1i(0, 0, 64); + MMSP::grid<1,MMSP::scalar > g1l(0, 0, 64); + MMSP::grid<1,MMSP::scalar > g1f(0, 0, 64); + MMSP::grid<1,MMSP::scalar > g1d(0, 0, 64); + + #ifndef MPI_VERSION + TS_ASSERT_EQUALS(MMSP::nodes(g1c),64); + TS_ASSERT_EQUALS(MMSP::nodes(g1s),64); + TS_ASSERT_EQUALS(MMSP::nodes(g1i),64); + TS_ASSERT_EQUALS(MMSP::nodes(g1l),64); + TS_ASSERT_EQUALS(MMSP::nodes(g1f),64); + TS_ASSERT_EQUALS(MMSP::nodes(g1d),64); + #else + int np = MPI::COMM_WORLD.Get_size(); + TS_ASSERT_LESS_THAN_EQUALS(MMSP::nodes(g1c),64/np); + TS_ASSERT_LESS_THAN_EQUALS(MMSP::nodes(g1s),64/np); + TS_ASSERT_LESS_THAN_EQUALS(MMSP::nodes(g1i),64/np); + TS_ASSERT_LESS_THAN_EQUALS(MMSP::nodes(g1l),64/np); + TS_ASSERT_LESS_THAN_EQUALS(MMSP::nodes(g1f),64/np); + TS_ASSERT_LESS_THAN_EQUALS(MMSP::nodes(g1d),64/np); + #endif + + TS_ASSERT_EQUALS(g1c.buffer_size(),MMSP::nodes(g1c)*sizeof(char)); + TS_ASSERT_EQUALS(g1s.buffer_size(),MMSP::nodes(g1s)*sizeof(short)); + TS_ASSERT_EQUALS(g1i.buffer_size(),MMSP::nodes(g1i)*sizeof(int)); + TS_ASSERT_EQUALS(g1l.buffer_size(),MMSP::nodes(g1l)*sizeof(long)); + TS_ASSERT_EQUALS(g1f.buffer_size(),MMSP::nodes(g1f)*sizeof(float)); + TS_ASSERT_EQUALS(g1d.buffer_size(),MMSP::nodes(g1d)*sizeof(double)); + + } +}; diff --git a/test/Makefile b/test/Makefile index ac62925..fc751f4 100644 --- a/test/Makefile +++ b/test/Makefile @@ -28,9 +28,8 @@ parallel: test.cpp $(core) unitTestSuite: MMSP.unitTestSuite.hpp cxxtestgen --error-printer -o MMSP.unitTestSuite.cpp $< && \ - $(compiler) $(flags) --coverage -fprofile-arcs -ftest-coverage -fPIC -o $@ MMSP.unitTestSuite.cpp $(core) && \ - ./$@ && \ - gcovr -r . --html --html-details -o unitTestSuite.html + $(compiler) $(flags) -o $@ MMSP.unitTestSuite.cpp $(core) && \ + ./$@ clean: - rm -f test parallel MMSP.unitTestSuite.cpp unitTestSuite *.html *.gcno *.gcda + rm -f test parallel MMSP.unitTestSuite.cpp unitTestSuite From e8a0d0bca010220c52ed895ee3db13a98671b32b Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Mon, 1 Feb 2016 18:31:57 -0500 Subject: [PATCH 04/83] renovate Zener pinning --- .../anisotropic/Monte_Carlo/zener.cpp | 150 +++++++---- .../anisotropic/Monte_Carlo/zener.hpp | 2 +- .../anisotropic/phase_field/zener.cpp | 142 +++++------ .../anisotropic/phase_field/zener.hpp | 2 +- .../anisotropic/sparsePF/zener.cpp | 124 ++++----- .../anisotropic/sparsePF/zener.hpp | 2 +- .../isotropic/Monte_Carlo/Makefile | 4 +- .../isotropic/Monte_Carlo/zener.cpp | 239 +++++++++++++++++- .../isotropic/Monte_Carlo/zener.hpp | 174 +------------ .../isotropic/phase_field/Makefile | 4 +- .../isotropic/phase_field/zener.cpp | 127 +++++++++- .../isotropic/phase_field/zener.hpp | 128 +--------- .../zener_pinning/isotropic/sparsePF/Makefile | 4 +- .../isotropic/sparsePF/zener.cpp | 151 ++++++++++- .../isotropic/sparsePF/zener.hpp | 152 +---------- 15 files changed, 757 insertions(+), 648 deletions(-) diff --git a/examples/coarsening/zener_pinning/anisotropic/Monte_Carlo/zener.cpp b/examples/coarsening/zener_pinning/anisotropic/Monte_Carlo/zener.cpp index b19441c..1d089b2 100644 --- a/examples/coarsening/zener_pinning/anisotropic/Monte_Carlo/zener.cpp +++ b/examples/coarsening/zener_pinning/anisotropic/Monte_Carlo/zener.cpp @@ -1,4 +1,4 @@ -// zener.hpp +// zener.cpp // Anisotropic coarsening algorithms for 2D and 3D Monte Carlo methods // Questions/comments to gruberja@gmail.com (Jason Gruber) @@ -14,87 +14,133 @@ namespace MMSP{ void generate(int dim, const char* filename) { if (dim==1) { - MMSP::grid<1,int> grid(0,0,128); + grid<1,int> initGrid(0,0,128); - for (int i=0; i x = position(grid,i); + for (int i=0; i x = position(initGrid,i); double d = 64.0-x[0]; - if (d<32.0) grid(i) = 2; - else grid(i) = 1; + if (d<32.0) initGrid(i) = 2; + else initGrid(i) = 1; } for (int j=0; j<50; j++) { - int i = rand()%nodes(grid); - vector p = position(grid,i); + int i = rand()%nodes(initGrid); + vector p = position(initGrid,i); for (int x=p[0]-1; x<=p[0]+1; x++) - grid[x] = 0; + initGrid[x] = 0; } - output(grid,filename); + output(initGrid,filename); } if (dim==2) { - MMSP::grid<2,int> grid(0,0,128,0,128); + grid<2,int> initGrid(0,0,128,0,128); - for (int i=0; i x = position(grid,i); + for (int i=0; i x = position(initGrid,i); double d = sqrt(pow(64.0-x[0],2)+pow(64.0-x[1],2)); - if (d<32.0) grid(i) = 2; - else grid(i) = 1; + if (d<32.0) initGrid(i) = 2; + else initGrid(i) = 1; } for (int j=0; j<50; j++) { - int i = rand()%nodes(grid); - vector p = position(grid,i); + int i = rand()%nodes(initGrid); + vector p = position(initGrid,i); for (int x=p[0]-1; x<=p[0]+1; x++) for (int y=p[1]-1; y<=p[1]+1; y++) - grid[x][y] = 0; + initGrid[x][y] = 0; } - output(grid,filename); + output(initGrid,filename); } if (dim==3) { - MMSP::grid<3,int> grid(0,0,64,0,64,0,64); + grid<3,int> initGrid(0,0,64,0,64,0,64); - for (int i=0; i x = position(grid,i); + for (int i=0; i x = position(initGrid,i); double d = sqrt(pow(32.0-x[0],2)+pow(32.0-x[1],2)+pow(32.0-x[2],2)); - if (d<16.0) grid(i) = 2; - else grid(i) = 1; + if (d<16.0) initGrid(i) = 2; + else initGrid(i) = 1; } for (int j=0; j<50; j++) { - int i = rand()%nodes(grid); - vector p = position(grid,i); + int i = rand()%nodes(initGrid); + vector p = position(initGrid,i); for (int x=p[0]-1; x<=p[0]+1; x++) for (int y=p[1]-1; y<=p[1]+1; y++) for (int z=p[2]-1; z<=p[2]+1; z++) - grid[x][y][z] = 0; + initGrid[x][y][z] = 0; } - output(grid,filename); + output(initGrid,filename); } } -void update(MMSP::grid<2,int>& grid, int steps) +void update(grid<1,int>& mcGrid, int steps) { const double kT = 0.50; - int gss = int(sqrt(nodes(grid))); + int gss = int(nodes(mcGrid)); for (int step=0; step x = position(grid,p); - int spin1 = grid(p); + for (int h=0; h x = position(mcGrid,p); + int spin1 = mcGrid(p); + + if (spin1!=0) { + // determine neighboring spins + sparse neighbors; + for (int i=-1; i<=1; i++) { + int spin = mcGrid[x[0]+i]; + set(neighbors,spin) = true; + } + + // choose a random neighbor spin + int spin2 = index(neighbors,rand()%length(neighbors)); + + if (spin1!=spin2 and spin2!=0) { + // compute energy change + double dE = -energy(spin1,spin2); + for (int i=-1; i<=1; i++) { + int spin = mcGrid[x[0]+i]; + dE += energy(spin,spin2)-energy(spin,spin1); + } + + // compute boundary energy, mobility + double E = energy(spin1,spin2); + double M = mobility(spin1,spin2); + + // attempt a spin flip + double r = double(rand())/double(RAND_MAX); + if (dE<=0.0 and r0.0 and r& mcGrid, int steps) +{ + const double kT = 0.50; + int gss = int(sqrt(nodes(mcGrid))); + + for (int step=0; step x = position(mcGrid,p); + int spin1 = mcGrid(p); if (spin1!=0) { // determine neighboring spins sparse neighbors; for (int i=-1; i<=1; i++) for (int j=-1; j<=1; j++) { - int spin = grid[x[0]+i][x[1]+j]; + int spin = mcGrid[x[0]+i][x[1]+j]; set(neighbors,spin) = true; } @@ -106,7 +152,7 @@ void update(MMSP::grid<2,int>& grid, int steps) double dE = -energy(spin1,spin2); for (int i=-1; i<=1; i++) for (int j=-1; j<=1; j++){ - int spin = grid[x[0]+i][x[1]+j]; + int spin = mcGrid[x[0]+i][x[1]+j]; dE += energy(spin,spin2)-energy(spin,spin1); } @@ -116,26 +162,26 @@ void update(MMSP::grid<2,int>& grid, int steps) // attempt a spin flip double r = double(rand())/double(RAND_MAX); - if (dE<=0.0 and r0.0 and r0.0 and r& grid, int steps) +void update(grid<3,int>& mcGrid, int steps) { const double kT = 0.75; - int gss = int(sqrt(nodes(grid))); + int gss = int(sqrt(nodes(mcGrid))); for (int step=0; step x = position(grid,p); - int spin1 = grid(p); + for (int h=0; h x = position(mcGrid,p); + int spin1 = mcGrid(p); if (spin1!=0) { // determine neighboring spins @@ -143,7 +189,7 @@ void update(MMSP::grid<3,int>& grid, int steps) for (int i=-1; i<=1; i++) for (int j=-1; j<=1; j++) for (int k=-1; k<=1; k++) { - int spin = grid[x[0]+i][x[1]+j][x[2]+k]; + int spin = mcGrid[x[0]+i][x[1]+j][x[2]+k]; set(neighbors,spin) = true; } @@ -156,7 +202,7 @@ void update(MMSP::grid<3,int>& grid, int steps) for (int i=-1; i<=1; i++) for (int j=-1; j<=1; j++) for (int k=-1; k<=1; k++) { - int spin = grid[x[0]+i][x[1]+j][x[2]+k]; + int spin = mcGrid[x[0]+i][x[1]+j][x[2]+k]; dE += energy(spin,spin2)-energy(spin,spin1); } @@ -166,11 +212,11 @@ void update(MMSP::grid<3,int>& grid, int steps) // attempt a spin flip double r = double(rand())/double(RAND_MAX); - if (dE<=0.0 and r0.0 and r0.0 and r > grid(3,0,128); + grid<1,vector > initGrid(3,0,128); - for (int i=0; i x = position(grid,i); - grid(i)[0] = 0.0; - grid(i)[1] = 0.0; + for (int i=0; i x = position(initGrid,i); + initGrid(i)[0] = 0.0; + initGrid(i)[1] = 0.0; double d = 64.0-x[0]; - if (d<32.0) grid(i)[2] = 1.0; - else grid(i)[1] = 1.0; + if (d<32.0) initGrid(i)[2] = 1.0; + else initGrid(i)[1] = 1.0; } for (int j=0; j<50; j++) { - int i = rand()%nodes(grid); - vector p = position(grid,i); + int i = rand()%nodes(initGrid); + vector p = position(initGrid,i); for (int x=p[0]-1; x<=p[0]+1; x++) { - grid[x][0] = 1.0; - grid[x][1] = 0.0; - grid[x][2] = 0.0; + initGrid[x][0] = 1.0; + initGrid[x][1] = 0.0; + initGrid[x][2] = 0.0; } } - output(grid,filename); + output(initGrid,filename); } if (dim==2) { - MMSP::grid<2,vector > grid(3,0,128,0,128); + grid<2,vector > initGrid(3,0,128,0,128); - for (int i=0; i x = position(grid,i); - grid(i)[0] = 0.0; - grid(i)[1] = 0.0; + for (int i=0; i x = position(initGrid,i); + initGrid(i)[0] = 0.0; + initGrid(i)[1] = 0.0; double d = sqrt(pow(64.0-x[0],2)+pow(64.0-x[1],2)); - if (d<32.0) grid(i)[2] = 1.0; - else grid(i)[1] = 1.0; + if (d<32.0) initGrid(i)[2] = 1.0; + else initGrid(i)[1] = 1.0; } for (int j=0; j<50; j++) { - int i = rand()%nodes(grid); - vector p = position(grid,i); + int i = rand()%nodes(initGrid); + vector p = position(initGrid,i); for (int x=p[0]-1; x<=p[0]+1; x++) for (int y=p[1]-1; y<=p[1]+1; y++) { - grid[x][y][0] = 1.0; - grid[x][y][1] = 0.0; - grid[x][y][2] = 0.0; + initGrid[x][y][0] = 1.0; + initGrid[x][y][1] = 0.0; + initGrid[x][y][2] = 0.0; } } - output(grid,filename); + output(initGrid,filename); } if (dim==3) { - MMSP::grid<3,vector > grid(3,0,64,0,64,0,64); + grid<3,vector > initGrid(3,0,64,0,64,0,64); - for (int i=0; i x = position(grid,i); - grid(i)[0] = 0.0; - grid(i)[1] = 0.0; + for (int i=0; i x = position(initGrid,i); + initGrid(i)[0] = 0.0; + initGrid(i)[1] = 0.0; double d = sqrt(pow(32.0-x[0],2)+pow(32.0-x[1],2)+pow(32.0-x[2],2)); - if (d<16.0) grid(i)[2] = 1.0; - else grid(i)[1] = 1.0; + if (d<16.0) initGrid(i)[2] = 1.0; + else initGrid(i)[1] = 1.0; } for (int j=0; j<50; j++) { - int i = rand()%nodes(grid); - vector p = position(grid,i); + int i = rand()%nodes(initGrid); + vector p = position(initGrid,i); for (int x=p[0]-1; x<=p[0]+1; x++) for (int y=p[1]-1; y<=p[1]+1; y++) for (int z=p[1]-1; z<=p[2]+1; z++) { - grid[x][y][z][0] = 1.0; - grid[x][y][z][1] = 0.0; - grid[x][y][z][2] = 0.0; + initGrid[x][y][z][0] = 1.0; + initGrid[x][y][z][1] = 0.0; + initGrid[x][y][z][2] = 0.0; } } - output(grid,filename); + output(initGrid,filename); } } -template void update(MMSP::grid >& grid, int steps) +template void update(grid >& oldGrid, int steps) { - MMSP::grid > update(grid); + grid > newGrid(oldGrid); double dt = 0.01; double width = 8.0; for (int step=0; step s(fields(grid),0); - vector x = position(grid,i); + vector s(fields(oldGrid),0); + vector x = position(oldGrid,i); - for (int h=0; h0.0) { + if (oldGrid(x)[h]>0.0) { s[h] = 1; x[j] -= k; goto next; @@ -122,37 +122,37 @@ template void update(MMSP::grid >& grid, int steps) } // if only one field is nonzero, - // then copy this node to update - if (S<2.0) update(i) = grid(i); + // then copy this node to newGrid + if (S<2.0) newGrid(i) = oldGrid(i); else { // compute laplacian of each field - vector lap = laplacian(grid,i); + vector lap = laplacian(oldGrid,i); // compute variational derivatives - vector dFdp(fields(grid),0.0); - for (int h=0; h dFdp(fields(oldGrid),0.0); + for (int h=0; h0.0) - for (int j=h+1; j0.0) { double gamma = energy(h,j); double eps = 4.0/acos(-1.0)*sqrt(0.5*gamma*width); double w = 4.0*gamma/width; - dFdp[h] += 0.5*eps*eps*lap[j]+w*grid(i)[j]; - dFdp[j] += 0.5*eps*eps*lap[h]+w*grid(i)[h]; - for (int k=j+1; k0.0) { - dFdp[h] += 3.0*grid(i)[j]*grid(i)[k]; - dFdp[j] += 3.0*grid(i)[k]*grid(i)[h]; - dFdp[k] += 3.0*grid(i)[h]*grid(i)[j]; + dFdp[h] += 3.0*oldGrid(i)[j]*oldGrid(i)[k]; + dFdp[j] += 3.0*oldGrid(i)[k]*oldGrid(i)[h]; + dFdp[k] += 3.0*oldGrid(i)[h]*oldGrid(i)[j]; } } // compute time derivatives - vector dpdt(fields(grid),0.0); - for (int h=0; h dpdt(fields(oldGrid),0.0); + for (int h=0; h0.0) - for (int j=h+1; j0.0) { double mu = mobility(h,j); // set mobility of particles to zero @@ -161,25 +161,25 @@ template void update(MMSP::grid >& grid, int steps) dpdt[j] -= mu*(dFdp[j]-dFdp[h]); } - // compute update values + // compute newGrid values double sum = 0.0; - for (int h=0; h1.0) value = 1.0; if (value<0.0) value = 0.0; - update(i)[h] = value; + newGrid(i)[h] = value; sum += value; } // project onto Gibbs simplex double rsum = 0.0; if (fabs(sum)>0.0) rsum = 1.0/sum; - for (int h=0; h > grid(0,0,128); + grid<1,sparse > initGrid(0,0,128); - for (int i=0; i x = position(grid,i); + for (int i=0; i x = position(initGrid,i); double d = 64.0-x[0]; - if (d<32.0) set(grid(i),2) = 1.0; - else set(grid(i),1) = 1.0; + if (d<32.0) set(initGrid(i),2) = 1.0; + else set(initGrid(i),1) = 1.0; } for (int j=0; j<50; j++) { - int i = rand()%nodes(grid); - vector p = position(grid,i); + int i = rand()%nodes(initGrid); + vector p = position(initGrid,i); for (int x=p[0]-1; x<=p[0]+1; x++) { - set(grid[x],0) = 1.0; - set(grid[x],1) = 0.0; - set(grid[x],2) = 0.0; + set(initGrid[x],0) = 1.0; + set(initGrid[x],1) = 0.0; + set(initGrid[x],2) = 0.0; } } - output(grid,filename); + output(initGrid,filename); } if (dim==2) { - MMSP::grid<2,sparse > grid(0,0,128,0,128); + grid<2,sparse > initGrid(0,0,128,0,128); - for (int i=0; i x = position(grid,i); + for (int i=0; i x = position(initGrid,i); double d = sqrt(pow(64.0-x[0],2)+pow(64.0-x[1],2)); - if (d<32.0) set(grid(i),2) = 1.0; - else set(grid(i),1) = 1.0; + if (d<32.0) set(initGrid(i),2) = 1.0; + else set(initGrid(i),1) = 1.0; } for (int j=0; j<50; j++) { - int i = rand()%nodes(grid); - vector p = position(grid,i); + int i = rand()%nodes(initGrid); + vector p = position(initGrid,i); for (int x=p[0]-1; x<=p[0]+1; x++) for (int y=p[1]-1; y<=p[1]+1; y++) { - set(grid[x][y],0) = 1.0; - set(grid[x][y],1) = 0.0; - set(grid[x][y],2) = 0.0; + set(initGrid[x][y],0) = 1.0; + set(initGrid[x][y],1) = 0.0; + set(initGrid[x][y],2) = 0.0; } } - output(grid,filename); + output(initGrid,filename); } if (dim==3) { - MMSP::grid<3,sparse > grid(0,0,64,0,64,0,64); + grid<3,sparse > initGrid(0,0,64,0,64,0,64); - for (int i=0; i x = position(grid,i); + for (int i=0; i x = position(initGrid,i); double d = sqrt(pow(32.0-x[0],2)+pow(32.0-x[1],2)+pow(32.0-x[2],2)); - if (d<16.0) set(grid(i),2) = 1.0; - else set(grid(i),1) = 1.0; + if (d<16.0) set(initGrid(i),2) = 1.0; + else set(initGrid(i),1) = 1.0; } for (int j=0; j<50; j++) { - int i = rand()%nodes(grid); - vector p = position(grid,i); + int i = rand()%nodes(initGrid); + vector p = position(initGrid,i); for (int x=p[0]-1; x<=p[0]+1; x++) for (int y=p[1]-1; y<=p[1]+1; y++) for (int z=p[1]-1; z<=p[2]+1; z++) { - set(grid[x][y][z],0) = 1.0; - set(grid[x][y][z],1) = 0.0; - set(grid[x][y][z],2) = 0.0; + set(initGrid[x][y][z],0) = 1.0; + set(initGrid[x][y][z],1) = 0.0; + set(initGrid[x][y][z],2) = 0.0; } } - output(grid,filename); + output(initGrid,filename); } } -template void update(MMSP::grid >& grid, int steps) +template void update(grid >& oldGrid, int steps) { double dt = 0.01; double width = 8.0; double epsilon = 1.0e-8; for (int step=0; step > update(grid); + grid > newGrid(oldGrid); - for (int i=0; i s; - vector x = position(grid,i); + vector x = position(oldGrid,n); for (int j=0; j lap = laplacian(grid,i); + sparse lap = laplacian(oldGrid,n); // compute variational derivatives sparse dFdp; for (int h=0; h void update(MMSP::grid >& grid, int steps) // compute time derivatives sparse dpdt; for (int h=0; h void update(MMSP::grid >& grid, int steps) // compute update values double sum = 0.0; for (int h=0; h1.0) value = 1.0; if (value<0.0) value = 0.0; - if (value>epsilon) set(update(i),index) = value; - sum += update(i)[index]; + if (value>epsilon) set(newGrid(n),i) = value; + sum += newGrid(n)[i]; } // project onto Gibbs simplex double rsum = 0.0; if (fabs(sum)>0.0) rsum = 1.0/sum; - for (int h=0; h #include"zener.hpp" -std::string PROGRAM = "zener"; -std::string MESSAGE = "Isotropic Monte Carlo grain growth with Zener pinning example code"; +namespace MMSP{ -typedef MMSP::grid<1,int> GRID1D; -typedef MMSP::grid<2,int> GRID2D; -typedef MMSP::grid<3,int> GRID3D; +void generate(int dim, const char* filename) +{ + if (dim==1) { + GRID1D grid(0,0,128); + + for (int i=0; i x = position(grid,i); + double d = 64.0-x[0]; + if (d<32.0) grid(i) = 2; + else grid(i) = 1; + } + + for (int j=0; j<50; j++) { + int i = rand()%nodes(grid); + vector x = position(grid,i); + grid(x) = 0; + for (int d=0; d x = position(grid,i); + double d = sqrt(pow(64.0-x[0],2)+pow(64.0-x[1],2)); + if (d<32.0) grid(i) = 2; + else grid(i) = 1; + } + + for (int j=0; j<50; j++) { + int i = rand()%nodes(grid); + vector x = position(grid,i); + grid(x) = 0; + for (int d=0; d x = position(grid,i); + double d = sqrt(pow(32.0-x[0],2)+pow(32.0-x[1],2)+pow(32.0-x[2],2)); + if (d<16.0) grid(i) = 2; + else grid(i) = 1; + } + + for (int j=0; j<50; j++) { + int i = rand()%nodes(grid); + vector x = position(grid,i); + grid(x) = 0; + for (int d=0; d void update(MMSP::grid<1,T>& grid, int steps) +{ + const double kT = 0.50; + int gss = int(sqrt(nodes(grid))); + + for (int step=0; step x = position(grid,p); + // determine neighboring spins + sparse neighbors; + set(neighbors,grid(x)) = true; + for (int d=0; d<1; d++) { + x[d]--; + set(neighbors,grid(x)) = true; + x[d]+=2; + set(neighbors,grid(x)) = true; + x[d]--; + } + + // choose a random neighbor spin + int spin2 = index(neighbors,rand()%length(neighbors)); + + if (spin1!=spin2 and spin2!=0) { + // compute energy change + double dE = -1.0; + for (int i=-1; i<=1; i++) { + int spin = grid[x[0]+i]; + dE += (spin!=spin2)-(spin!=spin1); + } + + // attempt a spin flip + double r = double(rand())/double(RAND_MAX); + if (dE<=0.0) grid(p) = spin2; + else if (r void update(MMSP::grid<2,T>& grid, int steps) +{ + const double kT = 0.50; + int gss = int(sqrt(nodes(grid))); + + for (int step=0; step x = position(grid,p); + // determine neighboring spins + sparse neighbors; + set(neighbors,grid(x)) = true; + for (int d=0; d<2; d++) { + x[d]--; + set(neighbors,grid(x)) = true; + x[d]+=2; + set(neighbors,grid(x)) = true; + x[d]--; + } + + // choose a random neighbor spin + int spin2 = index(neighbors,rand()%length(neighbors)); + + if (spin1!=spin2 and spin2!=0) { + // compute energy change + double dE = -1.0; + for (int i=-1; i<=1; i++) + for (int j=-1; j<=1; j++) { + int spin = grid[x[0]+i][x[1]+j]; + dE += (spin!=spin2)-(spin!=spin1); + } + + // attempt a spin flip + double r = double(rand())/double(RAND_MAX); + if (dE<=0.0) grid(p) = spin2; + else if (r void update(MMSP::grid<3,T>& grid, int steps) +{ + const double kT = 0.75; + int gss = int(sqrt(nodes(grid))); + + for (int step=0; step x = position(grid,p); + // determine neighboring spins + sparse neighbors; + set(neighbors,grid(x)) = true; + for (int d=0; d<3; d++) { + x[d]--; + set(neighbors,grid(x)) = true; + x[d]+=2; + set(neighbors,grid(x)) = true; + x[d]--; + } + + // choose a random neighbor spin + int spin2 = index(neighbors,rand()%length(neighbors)); + + if (spin1!=spin2 and spin2!=0) { + // compute energy change + double dE = -1.0; + for (int i=-1; i<=1; i++) + for (int j=-1; j<=1; j++) + for (int k=-1; k<=1; k++) { + int spin = grid[x[0]+i][x[1]+j][x[2]+k]; + dE += (spin!=spin2)-(spin!=spin1); + } + + // attempt a spin flip + double r = double(rand())/double(RAND_MAX); + if (dE<=0.0) grid(p) = spin2; + else if (r +std::string PROGRAM = "zener"; +std::string MESSAGE = "Isotropic Monte Carlo grain growth with Zener pinning example code"; -namespace MMSP{ - -void generate(int dim, const char* filename) -{ - if (dim==1) { - MMSP::grid<1,int> grid(0,0,128); - - for (int i=0; i x = position(grid,i); - double d = 64.0-x[0]; - if (d<32.0) grid(i) = 2; - else grid(i) = 1; - } - - for (int j=0; j<50; j++) { - int i = rand()%nodes(grid); - vector p = position(grid,i); - for (int x=p[0]-1; x<=p[0]+1; x++) - grid[x] = 0; - } - - output(grid,filename); - } - - if (dim==2) { - MMSP::grid<2,int> grid(0,0,128,0,128); - - for (int i=0; i x = position(grid,i); - double d = sqrt(pow(64.0-x[0],2)+pow(64.0-x[1],2)); - if (d<32.0) grid(i) = 2; - else grid(i) = 1; - } - - for (int j=0; j<50; j++) { - int i = rand()%nodes(grid); - vector p = position(grid,i); - for (int x=p[0]-1; x<=p[0]+1; x++) - for (int y=p[1]-1; y<=p[1]+1; y++) - grid[x][y] = 0; - } - - output(grid,filename); - } - - if (dim==3) { - MMSP::grid<3,int> grid(0,0,64,0,64,0,64); - - for (int i=0; i x = position(grid,i); - double d = sqrt(pow(32.0-x[0],2)+pow(32.0-x[1],2)+pow(32.0-x[2],2)); - if (d<16.0) grid(i) = 2; - else grid(i) = 1; - } - - for (int j=0; j<50; j++) { - int i = rand()%nodes(grid); - vector p = position(grid,i); - for (int x=p[0]-1; x<=p[0]+1; x++) - for (int y=p[1]-1; y<=p[1]+1; y++) - for (int z=p[2]-1; z<=p[2]+1; z++) - grid[x][y][z] = 0; - } - - output(grid,filename); - } -} - -void update(MMSP::grid<2,int>& grid, int steps) -{ - const double kT = 0.50; - int gss = int(sqrt(nodes(grid))); - - for (int step=0; step x = position(grid,p); - int spin1 = grid(p); - - if (spin1!=0) { - // determine neighboring spins - sparse neighbors; - for (int i=-1; i<=1; i++) - for (int j=-1; j<=1; j++) { - int spin = grid[x[0]+i][x[1]+j]; - set(neighbors,spin) = true; - } - - // choose a random neighbor spin - int spin2 = index(neighbors,rand()%length(neighbors)); - - if (spin1!=spin2 and spin2!=0) { - // compute energy change - double dE = -1.0; - for (int i=-1; i<=1; i++) - for (int j=-1; j<=1; j++) { - int spin = grid[x[0]+i][x[1]+j]; - dE += (spin!=spin2)-(spin!=spin1); - } - - // attempt a spin flip - double r = double(rand())/double(RAND_MAX); - if (dE<=0.0) grid(p) = spin2; - else if (r& grid, int steps) -{ - const double kT = 0.75; - int gss = int(sqrt(nodes(grid))); - - for (int step=0; step x = position(grid,p); - int spin1 = grid(p); - - if (spin1!=0) { - // determine neighboring spins - sparse neighbors; - for (int i=-1; i<=1; i++) - for (int j=-1; j<=1; j++) - for (int k=-1; k<=1; k++) { - int spin = grid[x[0]+i][x[1]+j][x[2]+k]; - set(neighbors,spin) = true; - } - - // choose a random neighbor spin - int spin2 = index(neighbors,rand()%length(neighbors)); - - if (spin1!=spin2 and spin2!=0) { - // compute energy change - double dE = -1.0; - for (int i=-1; i<=1; i++) - for (int j=-1; j<=1; j++) - for (int k=-1; k<=1; k++) { - int spin = grid[x[0]+i][x[1]+j][x[2]+k]; - dE += (spin!=spin2)-(spin!=spin1); - } - - // attempt a spin flip - double r = double(rand())/double(RAND_MAX); - if (dE<=0.0) grid(p) = spin2; - else if (r GRID1D; +typedef MMSP::grid<2,int> GRID2D; +typedef MMSP::grid<3,int> GRID3D; diff --git a/examples/coarsening/zener_pinning/isotropic/phase_field/Makefile b/examples/coarsening/zener_pinning/isotropic/phase_field/Makefile index ed0c575..ec1ef0e 100644 --- a/examples/coarsening/zener_pinning/isotropic/phase_field/Makefile +++ b/examples/coarsening/zener_pinning/isotropic/phase_field/Makefile @@ -18,10 +18,10 @@ core = $(incdir)/MMSP.main.hpp \ $(incdir)/MMSP.vector.hpp # the program -zener: zener.cpp zener.hpp $(core) +zener: zener.cpp $(core) $(compiler) $(flags) $< -o $@ -lz -parallel: zener.cpp zener.hpp $(core) +parallel: zener.cpp $(core) $(pcompiler) $(pflags) $< -o $@ -lz clean: diff --git a/examples/coarsening/zener_pinning/isotropic/phase_field/zener.cpp b/examples/coarsening/zener_pinning/isotropic/phase_field/zener.cpp index b4def68..b22c08d 100644 --- a/examples/coarsening/zener_pinning/isotropic/phase_field/zener.cpp +++ b/examples/coarsening/zener_pinning/isotropic/phase_field/zener.cpp @@ -1,15 +1,130 @@ // zener.cpp -// Isotropic phase field grain growth with Zener pinning example code +// Algorithms for 2D and 3D isotropic phase field growth with Zener pinning // Questions/comments to gruberja@gmail.com (Jason Gruber) +#ifndef ZENER_UPDATE +#define ZENER_UPDATE +#include"MMSP.hpp" +#include #include"zener.hpp" -std::string PROGRAM = "zener"; -std::string MESSAGE = "Isotropic phase field grain growth with Zener pinning example code"; +namespace MMSP{ -typedef MMSP::grid<1,MMSP::vector > GRID1D; -typedef MMSP::grid<2,MMSP::vector > GRID2D; -typedef MMSP::grid<3,MMSP::vector > GRID3D; +void generate(int dim, const char* filename) +{ + if (dim==1) { + grid<1,vector > initGrid(3,0,128); + + for (int i=0; i x = position(initGrid,i); + initGrid(i)[0] = 0.0; + initGrid(i)[1] = 0.0; + double d = 64.0-x[0]; + if (d<32.0) initGrid(i)[2] = 1.0; + else initGrid(i)[1] = 1.0; + } + + for (int j=0; j<50; j++) { + int i = rand()%nodes(initGrid); + vector p = position(initGrid,i); + for (int x=p[0]-1; x<=p[0]+1; x++) { + initGrid[x][0] = 1.0; + initGrid[x][1] = 0.0; + initGrid[x][2] = 0.0; + } + } + + output(initGrid,filename); + } + + if (dim==2) { + grid<2,vector > initGrid(3,0,128,0,128); + + for (int i=0; i x = position(initGrid,i); + initGrid(i)[0] = 0.0; + initGrid(i)[1] = 0.0; + double d = sqrt(pow(64.0-x[0],2)+pow(64.0-x[1],2)); + if (d<32.0) initGrid(i)[2] = 1.0; + else initGrid(i)[1] = 1.0; + } + + for (int j=0; j<50; j++) { + int i = rand()%nodes(initGrid); + vector p = position(initGrid,i); + for (int x=p[0]-1; x<=p[0]+1; x++) + for (int y=p[1]-1; y<=p[1]+1; y++) { + initGrid[x][y][0] = 1.0; + initGrid[x][y][1] = 0.0; + initGrid[x][y][2] = 0.0; + } + } + + output(initGrid,filename); + } + + if (dim==3) { + grid<3,vector > initGrid(3,0,64,0,64,0,64); + + for (int i=0; i x = position(initGrid,i); + initGrid(i)[0] = 0.0; + initGrid(i)[1] = 0.0; + double d = sqrt(pow(32.0-x[0],2)+pow(32.0-x[1],2)+pow(32.0-x[2],2)); + if (d<16.0) initGrid(i)[2] = 1.0; + else initGrid(i)[1] = 1.0; + } + + for (int j=0; j<50; j++) { + int i = rand()%nodes(initGrid); + vector p = position(initGrid,i); + for (int x=p[0]-1; x<=p[0]+1; x++) + for (int y=p[1]-1; y<=p[1]+1; y++) + for (int z=p[1]-1; z<=p[2]+1; z++) { + initGrid[x][y][z][0] = 1.0; + initGrid[x][y][z][1] = 0.0; + initGrid[x][y][z][2] = 0.0; + } + } + + output(initGrid,filename); + } +} + +template void update(grid >& oldGrid, int steps) +{ + grid > newGrid(oldGrid); + + double dt = 0.01; + + for (int step=0; step lap = laplacian(oldGrid,i); + + // compute sums of squares + double sum = 0.0; + for (int j=0; j +std::string PROGRAM = "zener"; +std::string MESSAGE = "Isotropic phase field grain growth with Zener pinning example code"; -namespace MMSP{ - -void generate(int dim, const char* filename) -{ - if (dim==1) { - MMSP::grid<1,vector > grid(3,0,128); - - for (int i=0; i x = position(grid,i); - grid(i)[0] = 0.0; - grid(i)[1] = 0.0; - double d = 64.0-x[0]; - if (d<32.0) grid(i)[2] = 1.0; - else grid(i)[1] = 1.0; - } - - for (int j=0; j<50; j++) { - int i = rand()%nodes(grid); - vector p = position(grid,i); - for (int x=p[0]-1; x<=p[0]+1; x++) { - grid[x][0] = 1.0; - grid[x][1] = 0.0; - grid[x][2] = 0.0; - } - } - - output(grid,filename); - } - - if (dim==2) { - MMSP::grid<2,vector > grid(3,0,128,0,128); - - for (int i=0; i x = position(grid,i); - grid(i)[0] = 0.0; - grid(i)[1] = 0.0; - double d = sqrt(pow(64.0-x[0],2)+pow(64.0-x[1],2)); - if (d<32.0) grid(i)[2] = 1.0; - else grid(i)[1] = 1.0; - } - - for (int j=0; j<50; j++) { - int i = rand()%nodes(grid); - vector p = position(grid,i); - for (int x=p[0]-1; x<=p[0]+1; x++) - for (int y=p[1]-1; y<=p[1]+1; y++) { - grid[x][y][0] = 1.0; - grid[x][y][1] = 0.0; - grid[x][y][2] = 0.0; - } - } - - output(grid,filename); - } - - if (dim==3) { - MMSP::grid<3,vector > grid(3,0,64,0,64,0,64); - - for (int i=0; i x = position(grid,i); - grid(i)[0] = 0.0; - grid(i)[1] = 0.0; - double d = sqrt(pow(32.0-x[0],2)+pow(32.0-x[1],2)+pow(32.0-x[2],2)); - if (d<16.0) grid(i)[2] = 1.0; - else grid(i)[1] = 1.0; - } - - for (int j=0; j<50; j++) { - int i = rand()%nodes(grid); - vector p = position(grid,i); - for (int x=p[0]-1; x<=p[0]+1; x++) - for (int y=p[1]-1; y<=p[1]+1; y++) - for (int z=p[1]-1; z<=p[2]+1; z++) { - grid[x][y][z][0] = 1.0; - grid[x][y][z][1] = 0.0; - grid[x][y][z][2] = 0.0; - } - } - - output(grid,filename); - } -} - -template void update(MMSP::grid >& grid, int steps) -{ - MMSP::grid > update(grid); - - double dt = 0.01; - - for (int step=0; step lap = laplacian(grid,i); - - // compute sums of squares - double sum = 0.0; - for (int j=0; j > GRID1D; +typedef MMSP::grid<2,MMSP::vector > GRID2D; +typedef MMSP::grid<3,MMSP::vector > GRID3D; diff --git a/examples/coarsening/zener_pinning/isotropic/sparsePF/Makefile b/examples/coarsening/zener_pinning/isotropic/sparsePF/Makefile index f4742cc..62b435c 100644 --- a/examples/coarsening/zener_pinning/isotropic/sparsePF/Makefile +++ b/examples/coarsening/zener_pinning/isotropic/sparsePF/Makefile @@ -18,10 +18,10 @@ core = $(incdir)/MMSP.main.hpp \ $(incdir)/MMSP.sparse.hpp # the program -zener: zener.cpp zener.hpp $(core) +zener: zener.cpp $(core) $(compiler) $(flags) $< -o $@ -lz -parallel: zener.cpp zener.hpp $(core) +parallel: zener.cpp $(core) $(pcompiler) $(pflags) $< -o $@ -lz clean: diff --git a/examples/coarsening/zener_pinning/isotropic/sparsePF/zener.cpp b/examples/coarsening/zener_pinning/isotropic/sparsePF/zener.cpp index 737683f..af282de 100644 --- a/examples/coarsening/zener_pinning/isotropic/sparsePF/zener.cpp +++ b/examples/coarsening/zener_pinning/isotropic/sparsePF/zener.cpp @@ -1,15 +1,152 @@ -// zener.cpp -// isotropic sparse phase field (sparsePF) model for Zener pinning using MMSP +// graingrowth.hpp +// Algorithms for 2D and 3D isotropic sparsePF grain growth // Questions/comments to gruberja@gmail.com (Jason Gruber) +#ifndef GRAINGROWTH_UPDATE +#define GRAINGROWTH_UPDATE +#include"MMSP.hpp" +#include #include"zener.hpp" -std::string PROGRAM = "zener"; -std::string MESSAGE = "isotropic sparse phase field (sparsePF) model for Zener pinning using MMSP"; +namespace MMSP{ -typedef MMSP::grid<1,MMSP::sparse > GRID1D; -typedef MMSP::grid<2,MMSP::sparse > GRID2D; -typedef MMSP::grid<3,MMSP::sparse > GRID3D; +void generate(int dim, const char* filename) +{ + if (dim==1) { + grid<1,sparse > initGrid(0,0,128); + + for (int i=0; i x = position(initGrid,i); + double d = 64.0-x[0]; + if (d<32.0) set(initGrid(i),2) = 1.0; + else set(initGrid(i),1) = 1.0; + } + + for (int j=0; j<50; j++) { + int i = rand()%nodes(initGrid); + vector p = position(initGrid,i); + for (int x=p[0]-1; x<=p[0]+1; x++) { + set(initGrid[x],0) = 1.0; + set(initGrid[x],1) = 0.0; + set(initGrid[x],2) = 0.0; + } + } + + output(initGrid,filename); + } + + if (dim==2) { + grid<2,sparse > initGrid(0,0,128,0,128); + + for (int i=0; i x = position(initGrid,i); + double d = sqrt(pow(64.0-x[0],2)+pow(64.0-x[1],2)); + if (d<32.0) set(initGrid(i),2) = 1.0; + else set(initGrid(i),1) = 1.0; + } + + for (int j=0; j<50; j++) { + int i = rand()%nodes(initGrid); + vector p = position(initGrid,i); + for (int x=p[0]-1; x<=p[0]+1; x++) + for (int y=p[1]-1; y<=p[1]+1; y++) { + set(initGrid[x][y],0) = 1.0; + set(initGrid[x][y],1) = 0.0; + set(initGrid[x][y],2) = 0.0; + } + } + + output(initGrid,filename); + } + + if (dim==3) { + grid<3,sparse > initGrid(0,0,64,0,64,0,64); + + for (int i=0; i x = position(initGrid,i); + double d = sqrt(pow(32.0-x[0],2)+pow(32.0-x[1],2)+pow(32.0-x[2],2)); + if (d<16.0) set(initGrid(i),2) = 1.0; + else set(initGrid(i),1) = 1.0; + } + + for (int j=0; j<50; j++) { + int i = rand()%nodes(initGrid); + vector p = position(initGrid,i); + for (int x=p[0]-1; x<=p[0]+1; x++) + for (int y=p[1]-1; y<=p[1]+1; y++) + for (int z=p[1]-1; z<=p[2]+1; z++) { + set(initGrid[x][y][z],0) = 1.0; + set(initGrid[x][y][z],1) = 0.0; + set(initGrid[x][y][z],2) = 0.0; + } + } + + output(initGrid,filename); + } +} + +template void update(grid >& oldGrid, int steps) +{ + double dt = 0.01; + double epsilon = 1.0e-8; + + for (int step=0; step > newGrid(oldGrid); + + for (int n=0; n x = position(oldGrid,n); + + // determine nonzero fields within + // the neighborhood of this node + sparse neighbors; + for (int j=0; j lap = laplacian(oldGrid,n); + + // compute sums of squares + double sum = 0.0; + for (int j=0; jepsilon) set(newGrid(n),i) = value; + } + } + } + } + swap(oldGrid,newGrid); + ghostswap(oldGrid); + } +} + +} // namespace MMSP + +#endif #include"MMSP.main.hpp" diff --git a/examples/coarsening/zener_pinning/isotropic/sparsePF/zener.hpp b/examples/coarsening/zener_pinning/isotropic/sparsePF/zener.hpp index bf3da48..7572293 100644 --- a/examples/coarsening/zener_pinning/isotropic/sparsePF/zener.hpp +++ b/examples/coarsening/zener_pinning/isotropic/sparsePF/zener.hpp @@ -1,148 +1,10 @@ -// graingrowth.hpp -// Algorithms for 2D and 3D isotropic sparsePF grain growth +// zener.hpp +// isotropic sparse phase field (sparsePF) model for Zener pinning using MMSP // Questions/comments to gruberja@gmail.com (Jason Gruber) -#ifndef GRAINGROWTH_UPDATE -#define GRAINGROWTH_UPDATE -#include"MMSP.hpp" -#include +std::string PROGRAM = "zener"; +std::string MESSAGE = "isotropic sparse phase field (sparsePF) model for Zener pinning using MMSP"; -namespace MMSP{ - -void generate(int dim, const char* filename) -{ - if (dim==1) { - MMSP::grid<1,sparse > grid(0,0,128); - - for (int i=0; i x = position(grid,i); - double d = 64.0-x[0]; - if (d<32.0) set(grid(i),2) = 1.0; - else set(grid(i),1) = 1.0; - } - - for (int j=0; j<50; j++) { - int i = rand()%nodes(grid); - vector p = position(grid,i); - for (int x=p[0]-1; x<=p[0]+1; x++) { - set(grid[x],0) = 1.0; - set(grid[x],1) = 0.0; - set(grid[x],2) = 0.0; - } - } - - output(grid,filename); - } - - if (dim==2) { - MMSP::grid<2,sparse > grid(0,0,128,0,128); - - for (int i=0; i x = position(grid,i); - double d = sqrt(pow(64.0-x[0],2)+pow(64.0-x[1],2)); - if (d<32.0) set(grid(i),2) = 1.0; - else set(grid(i),1) = 1.0; - } - - for (int j=0; j<50; j++) { - int i = rand()%nodes(grid); - vector p = position(grid,i); - for (int x=p[0]-1; x<=p[0]+1; x++) - for (int y=p[1]-1; y<=p[1]+1; y++) { - set(grid[x][y],0) = 1.0; - set(grid[x][y],1) = 0.0; - set(grid[x][y],2) = 0.0; - } - } - - output(grid,filename); - } - - if (dim==3) { - MMSP::grid<3,sparse > grid(0,0,64,0,64,0,64); - - for (int i=0; i x = position(grid,i); - double d = sqrt(pow(32.0-x[0],2)+pow(32.0-x[1],2)+pow(32.0-x[2],2)); - if (d<16.0) set(grid(i),2) = 1.0; - else set(grid(i),1) = 1.0; - } - - for (int j=0; j<50; j++) { - int i = rand()%nodes(grid); - vector p = position(grid,i); - for (int x=p[0]-1; x<=p[0]+1; x++) - for (int y=p[1]-1; y<=p[1]+1; y++) - for (int z=p[1]-1; z<=p[2]+1; z++) { - set(grid[x][y][z],0) = 1.0; - set(grid[x][y][z],1) = 0.0; - set(grid[x][y][z],2) = 0.0; - } - } - - output(grid,filename); - } -} - -template void update(MMSP::grid >& grid, int steps) -{ - double dt = 0.01; - double epsilon = 1.0e-8; - - for (int step=0; step > update(grid); - - for (int i=0; i x = position(grid,i); - - // determine nonzero fields within - // the neighborhood of this node - sparse neighbors; - for (int j=0; j lap = laplacian(grid,i); - - // compute sums of squares - double sum = 0.0; - for (int j=0; jepsilon) set(update(i),index) = value; - } - } - } - } - swap(grid,update); - ghostswap(grid); - } -} - -} // namespace MMSP - -#endif +typedef MMSP::grid<1,MMSP::sparse > GRID1D; +typedef MMSP::grid<2,MMSP::sparse > GRID2D; +typedef MMSP::grid<3,MMSP::sparse > GRID3D; From 2965185d1d040ed608c5a8e593e364dff4e2f34d Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Mon, 1 Feb 2016 18:43:35 -0500 Subject: [PATCH 05/83] correct self-referenced filenames (cpp, hpp) --- .../grain_growth/anisotropic/Monte_Carlo/graingrowth.hpp | 2 +- .../grain_growth/anisotropic/phase_field/graingrowth.cpp | 2 +- .../grain_growth/anisotropic/phase_field/graingrowth.hpp | 2 +- .../grain_growth/anisotropic/sparsePF/graingrowth.cpp | 2 +- .../grain_growth/anisotropic/sparsePF/graingrowth.hpp | 2 +- .../grain_growth/isotropic/Monte_Carlo/graingrowth.hpp | 2 +- .../grain_growth/isotropic/phase_field/graingrowth.cpp | 2 +- .../grain_growth/isotropic/phase_field/graingrowth.hpp | 2 +- .../grain_growth/isotropic/sparsePF/graingrowth.cpp | 2 +- .../grain_growth/isotropic/sparsePF/graingrowth.hpp | 2 +- .../ostwald_ripening/isotropic/phase_field/ostwald.cpp | 2 +- .../ostwald_ripening/isotropic/phase_field/ostwald.hpp | 2 +- .../coarsening/zener_pinning/anisotropic/sparsePF/zener.cpp | 2 +- .../coarsening/zener_pinning/isotropic/sparsePF/zener.cpp | 4 ++-- examples/differential_equations/elliptic/Poisson/poisson.cpp | 2 +- examples/differential_equations/elliptic/Poisson/poisson.hpp | 2 +- examples/phase_transitions/allen-cahn/allen-cahn.cpp | 2 +- examples/phase_transitions/allen-cahn/allen-cahn.hpp | 2 +- .../cahn-hilliard/convex_splitting/cahn-hilliard.cpp | 2 +- .../cahn-hilliard/convex_splitting/cahn-hilliard.hpp | 2 +- .../cahn-hilliard/explicit/cahn-hilliard.cpp | 2 +- .../cahn-hilliard/explicit/cahn-hilliard.hpp | 2 +- examples/phase_transitions/model_A/model_A.cpp | 2 +- examples/phase_transitions/model_A/model_A.hpp | 2 +- examples/phase_transitions/model_B/model_B.cpp | 2 +- examples/phase_transitions/model_B/model_B.hpp | 2 +- .../solidification/anisotropic/dendritic.cpp | 2 +- .../solidification/anisotropic/dendritic.hpp | 2 +- examples/phase_transitions/spinodal/spinodal.cpp | 2 +- examples/phase_transitions/spinodal/spinodal.hpp | 2 +- examples/statistical_mechanics/Heisenberg/heisenberg.cpp | 2 +- examples/statistical_mechanics/Heisenberg/heisenberg.hpp | 2 +- examples/statistical_mechanics/Ising/ising.cpp | 2 +- examples/statistical_mechanics/Ising/ising.hpp | 2 +- examples/statistical_mechanics/Potts/potts.cpp | 2 +- examples/statistical_mechanics/Potts/potts.hpp | 2 +- 36 files changed, 37 insertions(+), 37 deletions(-) diff --git a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.hpp b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.hpp index 355f3b3..d6add1d 100644 --- a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.hpp +++ b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.hpp @@ -1,4 +1,4 @@ -// graingrowth.cpp +// graingrowth.hpp // Anisotropic Monte Carlo grain growth example code // Questions/comments to gruberja@gmail.com (Jason Gruber) // Updated MC implementation by tany3@rpi.edu (Yixuan Tan) diff --git a/examples/coarsening/grain_growth/anisotropic/phase_field/graingrowth.cpp b/examples/coarsening/grain_growth/anisotropic/phase_field/graingrowth.cpp index b3d5f53..59eeefa 100644 --- a/examples/coarsening/grain_growth/anisotropic/phase_field/graingrowth.cpp +++ b/examples/coarsening/grain_growth/anisotropic/phase_field/graingrowth.cpp @@ -1,4 +1,4 @@ -// graingrowth.hpp +// graingrowth.cpp // Anisotropic coarsening algorithms for 2D and 3D phase field methods // Questions/comments to gruberja@gmail.com (Jason Gruber) diff --git a/examples/coarsening/grain_growth/anisotropic/phase_field/graingrowth.hpp b/examples/coarsening/grain_growth/anisotropic/phase_field/graingrowth.hpp index 61bfe1c..36472d2 100644 --- a/examples/coarsening/grain_growth/anisotropic/phase_field/graingrowth.hpp +++ b/examples/coarsening/grain_growth/anisotropic/phase_field/graingrowth.hpp @@ -1,4 +1,4 @@ -// graingrowth.cpp +// graingrowth.hpp // Anisotropic phase field grain growth example code // Questions/comments to gruberja@gmail.com (Jason Gruber) diff --git a/examples/coarsening/grain_growth/anisotropic/sparsePF/graingrowth.cpp b/examples/coarsening/grain_growth/anisotropic/sparsePF/graingrowth.cpp index 5d5ea76..91f36ca 100644 --- a/examples/coarsening/grain_growth/anisotropic/sparsePF/graingrowth.cpp +++ b/examples/coarsening/grain_growth/anisotropic/sparsePF/graingrowth.cpp @@ -1,4 +1,4 @@ -// graingrowth.hpp +// graingrowth.cpp // Anisotropic coarsening algorithms for 2D and 3D sparse phase field (sparsePF) methods // Questions/comments to gruberja@gmail.com (Jason Gruber) diff --git a/examples/coarsening/grain_growth/anisotropic/sparsePF/graingrowth.hpp b/examples/coarsening/grain_growth/anisotropic/sparsePF/graingrowth.hpp index 18b42d3..61eea7f 100644 --- a/examples/coarsening/grain_growth/anisotropic/sparsePF/graingrowth.hpp +++ b/examples/coarsening/grain_growth/anisotropic/sparsePF/graingrowth.hpp @@ -1,4 +1,4 @@ -// graingrowth.cpp +// graingrowth.hpp // Anisotropic sparse phase field (sparsePF) grain growth example code // Questions/comments to gruberja@gmail.com (Jason Gruber) diff --git a/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.hpp b/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.hpp index f72c8dc..27ef676 100644 --- a/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.hpp +++ b/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.hpp @@ -1,4 +1,4 @@ -// graingrowth.cpp +// graingrowth.hpp // Isotropic Monte Carlo grain growth example code // Questions/comments to gruberja@gmail.com (Jason Gruber) // Improved MC implementation by tany3@rpi.edu (Yixuan Tan) diff --git a/examples/coarsening/grain_growth/isotropic/phase_field/graingrowth.cpp b/examples/coarsening/grain_growth/isotropic/phase_field/graingrowth.cpp index 303f7c1..9024984 100644 --- a/examples/coarsening/grain_growth/isotropic/phase_field/graingrowth.cpp +++ b/examples/coarsening/grain_growth/isotropic/phase_field/graingrowth.cpp @@ -1,4 +1,4 @@ -// graingrowth.hpp +// graingrowth.cpp // Algorithms for 2D and 3D isotropic phase field grain growth // Questions/comments to gruberja@gmail.com (Jason Gruber) diff --git a/examples/coarsening/grain_growth/isotropic/phase_field/graingrowth.hpp b/examples/coarsening/grain_growth/isotropic/phase_field/graingrowth.hpp index a4a835a..578bc4f 100644 --- a/examples/coarsening/grain_growth/isotropic/phase_field/graingrowth.hpp +++ b/examples/coarsening/grain_growth/isotropic/phase_field/graingrowth.hpp @@ -1,4 +1,4 @@ -// graingrowth.cpp +// graingrowth.hpp // Isotropic phase field grain growth example code // Questions/comments to gruberja@gmail.com (Jason Gruber) diff --git a/examples/coarsening/grain_growth/isotropic/sparsePF/graingrowth.cpp b/examples/coarsening/grain_growth/isotropic/sparsePF/graingrowth.cpp index 6338213..ca407fe 100644 --- a/examples/coarsening/grain_growth/isotropic/sparsePF/graingrowth.cpp +++ b/examples/coarsening/grain_growth/isotropic/sparsePF/graingrowth.cpp @@ -1,4 +1,4 @@ -// graingrowth.hpp +// graingrowth.cpp // Algorithms for 2D and 3D isotropic sparsePF grain growth // Questions/comments to gruberja@gmail.com (Jason Gruber) diff --git a/examples/coarsening/grain_growth/isotropic/sparsePF/graingrowth.hpp b/examples/coarsening/grain_growth/isotropic/sparsePF/graingrowth.hpp index ced2a3b..e6ce96d 100644 --- a/examples/coarsening/grain_growth/isotropic/sparsePF/graingrowth.hpp +++ b/examples/coarsening/grain_growth/isotropic/sparsePF/graingrowth.hpp @@ -1,4 +1,4 @@ -// graingrowth.cpp +// graingrowth.hpp // Isotropic sparse phase field (sparsePF) grain growth example code // Questions/comments to gruberja@gmail.com (Jason Gruber) diff --git a/examples/coarsening/ostwald_ripening/isotropic/phase_field/ostwald.cpp b/examples/coarsening/ostwald_ripening/isotropic/phase_field/ostwald.cpp index 474c4ea..6deca0c 100644 --- a/examples/coarsening/ostwald_ripening/isotropic/phase_field/ostwald.cpp +++ b/examples/coarsening/ostwald_ripening/isotropic/phase_field/ostwald.cpp @@ -1,4 +1,4 @@ -// ostwald.hpp +// ostwald.cpp // Ostwald ripening algorithms for 2D and 3D phase field methods // Questions/comments to gruberja@gmail.com (Jason Gruber) diff --git a/examples/coarsening/ostwald_ripening/isotropic/phase_field/ostwald.hpp b/examples/coarsening/ostwald_ripening/isotropic/phase_field/ostwald.hpp index 3920d03..111204a 100644 --- a/examples/coarsening/ostwald_ripening/isotropic/phase_field/ostwald.hpp +++ b/examples/coarsening/ostwald_ripening/isotropic/phase_field/ostwald.hpp @@ -1,4 +1,4 @@ -// ostwald.cpp +// ostwald.hpp // Example program for Ostwald ripening using MMSP // Questions/comments to gruberja@gmail.com (Jason Gruber) diff --git a/examples/coarsening/zener_pinning/anisotropic/sparsePF/zener.cpp b/examples/coarsening/zener_pinning/anisotropic/sparsePF/zener.cpp index 3c08316..4202d84 100644 --- a/examples/coarsening/zener_pinning/anisotropic/sparsePF/zener.cpp +++ b/examples/coarsening/zener_pinning/anisotropic/sparsePF/zener.cpp @@ -1,4 +1,4 @@ -// zener.hpp +// zener.cpp // Anisotropic coarsening algorithms for 2D and 3D sparse phase field (sparsePF) methods // Questions/comments to gruberja@gmail.com (Jason Gruber) diff --git a/examples/coarsening/zener_pinning/isotropic/sparsePF/zener.cpp b/examples/coarsening/zener_pinning/isotropic/sparsePF/zener.cpp index af282de..7918a2f 100644 --- a/examples/coarsening/zener_pinning/isotropic/sparsePF/zener.cpp +++ b/examples/coarsening/zener_pinning/isotropic/sparsePF/zener.cpp @@ -1,5 +1,5 @@ -// graingrowth.hpp -// Algorithms for 2D and 3D isotropic sparsePF grain growth +// zener.cpp +// Algorithms for 2D and 3D isotropic sparsePF growth with Zener pinning // Questions/comments to gruberja@gmail.com (Jason Gruber) #ifndef GRAINGROWTH_UPDATE diff --git a/examples/differential_equations/elliptic/Poisson/poisson.cpp b/examples/differential_equations/elliptic/Poisson/poisson.cpp index f53254f..e13780d 100644 --- a/examples/differential_equations/elliptic/Poisson/poisson.cpp +++ b/examples/differential_equations/elliptic/Poisson/poisson.cpp @@ -1,4 +1,4 @@ -// poisson.hpp +// poisson.cpp // smooth() and defect() functions for multigrid solution of Poisson equation // Questions/comments to gruberja@gmail.com (Jason Gruber) diff --git a/examples/differential_equations/elliptic/Poisson/poisson.hpp b/examples/differential_equations/elliptic/Poisson/poisson.hpp index ae4a087..048f81d 100644 --- a/examples/differential_equations/elliptic/Poisson/poisson.hpp +++ b/examples/differential_equations/elliptic/Poisson/poisson.hpp @@ -1,4 +1,4 @@ -// poisson.cpp +// poisson.hpp // multigrid Poisson equation solver using MMSP // Questions/comments to gruberja@gmail.com (Jason Gruber) diff --git a/examples/phase_transitions/allen-cahn/allen-cahn.cpp b/examples/phase_transitions/allen-cahn/allen-cahn.cpp index 5239520..eb6aea0 100644 --- a/examples/phase_transitions/allen-cahn/allen-cahn.cpp +++ b/examples/phase_transitions/allen-cahn/allen-cahn.cpp @@ -1,4 +1,4 @@ -// allen-cahn.hpp +// allen-cahn.cpp // Algorithms for 2D and 3D Allen-Cahn model // Questions/comments to gruberja@gmail.com (Jason Gruber) diff --git a/examples/phase_transitions/allen-cahn/allen-cahn.hpp b/examples/phase_transitions/allen-cahn/allen-cahn.hpp index 5893db9..7cc6691 100644 --- a/examples/phase_transitions/allen-cahn/allen-cahn.hpp +++ b/examples/phase_transitions/allen-cahn/allen-cahn.hpp @@ -1,4 +1,4 @@ -// allen-cahn.cpp +// allen-cahn.hpp // Example program for the Allen-Cahn model using MMSP // Questions/comments to gruberja@gmail.com (Jason Gruber) diff --git a/examples/phase_transitions/cahn-hilliard/convex_splitting/cahn-hilliard.cpp b/examples/phase_transitions/cahn-hilliard/convex_splitting/cahn-hilliard.cpp index 21f671f..e27f662 100644 --- a/examples/phase_transitions/cahn-hilliard/convex_splitting/cahn-hilliard.cpp +++ b/examples/phase_transitions/cahn-hilliard/convex_splitting/cahn-hilliard.cpp @@ -1,4 +1,4 @@ -/** cahn-hilliard.hpp +/** cahn-hilliard.cpp ** Algorithms for 2D Cahn-Hilliard model with convex splitting. ** ** This is an advanced example, with precompiler directives switching between diff --git a/examples/phase_transitions/cahn-hilliard/convex_splitting/cahn-hilliard.hpp b/examples/phase_transitions/cahn-hilliard/convex_splitting/cahn-hilliard.hpp index c39bb6e..91f8805 100644 --- a/examples/phase_transitions/cahn-hilliard/convex_splitting/cahn-hilliard.hpp +++ b/examples/phase_transitions/cahn-hilliard/convex_splitting/cahn-hilliard.hpp @@ -1,4 +1,4 @@ -// cahn-hilliard.cpp +// cahn-hilliard.hpp // Example program for the Cahn-Hilliard model using MMSP // Questions/comments to gruberja@gmail.com (Jason Gruber) diff --git a/examples/phase_transitions/cahn-hilliard/explicit/cahn-hilliard.cpp b/examples/phase_transitions/cahn-hilliard/explicit/cahn-hilliard.cpp index e8db10e..d10c8ea 100644 --- a/examples/phase_transitions/cahn-hilliard/explicit/cahn-hilliard.cpp +++ b/examples/phase_transitions/cahn-hilliard/explicit/cahn-hilliard.cpp @@ -1,4 +1,4 @@ -// cahn-hilliard.hpp +// cahn-hilliard.cpp // Algorithms for 2D and 3D Cahn-Hilliard model // Questions/comments to gruberja@gmail.com (Jason Gruber) diff --git a/examples/phase_transitions/cahn-hilliard/explicit/cahn-hilliard.hpp b/examples/phase_transitions/cahn-hilliard/explicit/cahn-hilliard.hpp index 3500ae9..0472615 100644 --- a/examples/phase_transitions/cahn-hilliard/explicit/cahn-hilliard.hpp +++ b/examples/phase_transitions/cahn-hilliard/explicit/cahn-hilliard.hpp @@ -1,4 +1,4 @@ -// cahn-hilliard.cpp +// cahn-hilliard.hpp // Example program for the Cahn-Hilliard model using MMSP // Questions/comments to gruberja@gmail.com (Jason Gruber) diff --git a/examples/phase_transitions/model_A/model_A.cpp b/examples/phase_transitions/model_A/model_A.cpp index 0654c4f..c08aeac 100644 --- a/examples/phase_transitions/model_A/model_A.cpp +++ b/examples/phase_transitions/model_A/model_A.cpp @@ -1,4 +1,4 @@ -// model_A.hpp +// model_A.cpp // Algorithms for 2D and 3D implementation of "model A" // Questions/comments to gruberja@gmail.com (Jason Gruber) diff --git a/examples/phase_transitions/model_A/model_A.hpp b/examples/phase_transitions/model_A/model_A.hpp index 47843ed..09359f6 100644 --- a/examples/phase_transitions/model_A/model_A.hpp +++ b/examples/phase_transitions/model_A/model_A.hpp @@ -1,4 +1,4 @@ -// model_A.cpp +// model_A.hpp // Example program for "model A" using MMSP // Questions/comments to gruberja@gmail.com (Jason Gruber) diff --git a/examples/phase_transitions/model_B/model_B.cpp b/examples/phase_transitions/model_B/model_B.cpp index a8934ae..7bb9794 100644 --- a/examples/phase_transitions/model_B/model_B.cpp +++ b/examples/phase_transitions/model_B/model_B.cpp @@ -1,4 +1,4 @@ -// model_B.hpp +// model_B.cpp // Algorithms for 2D and 3D implementation of "model B" // Questions/comments to gruberja@gmail.com (Jason Gruber) diff --git a/examples/phase_transitions/model_B/model_B.hpp b/examples/phase_transitions/model_B/model_B.hpp index b520fbb..90e5c61 100644 --- a/examples/phase_transitions/model_B/model_B.hpp +++ b/examples/phase_transitions/model_B/model_B.hpp @@ -1,4 +1,4 @@ -// model_B.cpp +// model_B.hpp // Example program for "model B" using MMSP // Questions/comments to gruberja@gmail.com (Jason Gruber) diff --git a/examples/phase_transitions/solidification/anisotropic/dendritic.cpp b/examples/phase_transitions/solidification/anisotropic/dendritic.cpp index b05ab64..1f64eb1 100644 --- a/examples/phase_transitions/solidification/anisotropic/dendritic.cpp +++ b/examples/phase_transitions/solidification/anisotropic/dendritic.cpp @@ -1,4 +1,4 @@ -// dendritic.hpp +// dendritic.cpp // Anisotropic solidification by 2D phase field method // Questions/comments to kellet@rpi.edu (Trevor Keller) diff --git a/examples/phase_transitions/solidification/anisotropic/dendritic.hpp b/examples/phase_transitions/solidification/anisotropic/dendritic.hpp index 82a4d86..64a6764 100644 --- a/examples/phase_transitions/solidification/anisotropic/dendritic.hpp +++ b/examples/phase_transitions/solidification/anisotropic/dendritic.hpp @@ -1,4 +1,4 @@ -// dendritic.cpp +// dendritic.hpp // Anisotropic dendritic solidification code // Questions/comments to kellet@rpi.edu (Trevor Keller) diff --git a/examples/phase_transitions/spinodal/spinodal.cpp b/examples/phase_transitions/spinodal/spinodal.cpp index 194d65e..fe950c4 100644 --- a/examples/phase_transitions/spinodal/spinodal.cpp +++ b/examples/phase_transitions/spinodal/spinodal.cpp @@ -1,4 +1,4 @@ -// spinodal.hpp +// spinodal.cpp // Algorithm for 2D and 3D spinodal decomposition phase field model // Questions/comments to gruberja@gmail.com (Jason Gruber) diff --git a/examples/phase_transitions/spinodal/spinodal.hpp b/examples/phase_transitions/spinodal/spinodal.hpp index d644d96..dbd2017 100644 --- a/examples/phase_transitions/spinodal/spinodal.hpp +++ b/examples/phase_transitions/spinodal/spinodal.hpp @@ -1,4 +1,4 @@ -// spinodal.cpp +// spinodal.hpp // Example program for spinodal decomposition using MMSP // Questions/comments to gruberja@gmail.com (Jason Gruber) diff --git a/examples/statistical_mechanics/Heisenberg/heisenberg.cpp b/examples/statistical_mechanics/Heisenberg/heisenberg.cpp index 5306950..2bc31bd 100644 --- a/examples/statistical_mechanics/Heisenberg/heisenberg.cpp +++ b/examples/statistical_mechanics/Heisenberg/heisenberg.cpp @@ -1,4 +1,4 @@ -// heisenberg.hpp +// heisenberg.cpp // 2D and 3D Heisenberg model // Questions/comments to gruberja@gmail.com (Jason Gruber) diff --git a/examples/statistical_mechanics/Heisenberg/heisenberg.hpp b/examples/statistical_mechanics/Heisenberg/heisenberg.hpp index 5144fed..2d25d4c 100644 --- a/examples/statistical_mechanics/Heisenberg/heisenberg.hpp +++ b/examples/statistical_mechanics/Heisenberg/heisenberg.hpp @@ -1,4 +1,4 @@ -// heisenberg.cpp +// heisenberg.hpp // Classical Heisenberg model using MMSP // Questions/comments to gruberja@gmail.com (Jason Gruber) diff --git a/examples/statistical_mechanics/Ising/ising.cpp b/examples/statistical_mechanics/Ising/ising.cpp index 43f9ad8..c5803f5 100644 --- a/examples/statistical_mechanics/Ising/ising.cpp +++ b/examples/statistical_mechanics/Ising/ising.cpp @@ -1,4 +1,4 @@ -// ising.hpp +// ising.cpp // 2D and 3D Ising model // Questions/comments to gruberja@gmail.com (Jason Gruber) diff --git a/examples/statistical_mechanics/Ising/ising.hpp b/examples/statistical_mechanics/Ising/ising.hpp index a896b32..34d93f3 100644 --- a/examples/statistical_mechanics/Ising/ising.hpp +++ b/examples/statistical_mechanics/Ising/ising.hpp @@ -1,4 +1,4 @@ -// ising.cpp +// ising.hpp // Ising model using MMSP // Questions/comments to gruberja@gmail.com (Jason Gruber) diff --git a/examples/statistical_mechanics/Potts/potts.cpp b/examples/statistical_mechanics/Potts/potts.cpp index b479e84..1f006dd 100644 --- a/examples/statistical_mechanics/Potts/potts.cpp +++ b/examples/statistical_mechanics/Potts/potts.cpp @@ -1,4 +1,4 @@ -// potts.hpp +// potts.cpp // 2D and 3D Potts model // Questions/comments to gruberja@gmail.com (Jason Gruber) diff --git a/examples/statistical_mechanics/Potts/potts.hpp b/examples/statistical_mechanics/Potts/potts.hpp index 2d1a527..6a11f54 100644 --- a/examples/statistical_mechanics/Potts/potts.hpp +++ b/examples/statistical_mechanics/Potts/potts.hpp @@ -1,4 +1,4 @@ -// potts.cpp +// potts.hpp // Q-state Potts model using MMSP // Questions/comments to gruberja@gmail.com (Jason Gruber) From cfd3d11af2a91bb52081180abdec2935d3fad66f Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Mon, 1 Feb 2016 20:11:00 -0500 Subject: [PATCH 06/83] templating, typenames, arb dimensions --- .../beginners_diffusion/MMSPDiffusion.cpp | 2 +- .../beginners_diffusion/MMSPDiffusion2D.cpp | 2 +- .../anisotropic/Monte_Carlo/graingrowth.cpp | 122 +++++------ .../anisotropic/phase_field/graingrowth.cpp | 134 ++++++------ .../anisotropic/sparsePF/graingrowth.cpp | 120 +++++------ .../isotropic/Monte_Carlo/graingrowth.cpp | 100 ++++----- .../isotropic/phase_field/graingrowth.cpp | 70 +++---- .../isotropic/sparsePF/graingrowth.cpp | 72 +++---- .../isotropic/phase_field/ostwald.cpp | 198 +++++------------- .../anisotropic/Monte_Carlo/zener.cpp | 136 +++--------- .../anisotropic/phase_field/zener.cpp | 18 +- .../anisotropic/sparsePF/zener.cpp | 18 +- .../isotropic/phase_field/zener.cpp | 16 +- .../isotropic/sparsePF/zener.cpp | 18 +- .../allen-cahn/allen-cahn.cpp | 38 ++-- .../convex_splitting/cahn-hilliard.cpp | 48 ++--- .../cahn-hilliard/explicit/cahn-hilliard.cpp | 44 ++-- .../phase_transitions/model_A/model_A.cpp | 51 ++--- .../phase_transitions/model_B/model_B.cpp | 57 ++--- .../solidification/anisotropic/dendritic.cpp | 90 ++++---- .../phase_transitions/spinodal/spinodal.cpp | 49 +++-- .../Heisenberg/heisenberg.cpp | 129 +++++------- .../statistical_mechanics/Ising/ising.cpp | 98 ++++----- .../statistical_mechanics/Potts/potts.cpp | 98 ++++----- 24 files changed, 755 insertions(+), 973 deletions(-) diff --git a/examples/beginners_diffusion/MMSPDiffusion.cpp b/examples/beginners_diffusion/MMSPDiffusion.cpp index a92affd..ce1cfa6 100644 --- a/examples/beginners_diffusion/MMSPDiffusion.cpp +++ b/examples/beginners_diffusion/MMSPDiffusion.cpp @@ -1,7 +1,7 @@ #include"MMSP.hpp" using namespace MMSP; -// Add in Laplacian funciton that is built in to MMSP +// Add in Laplacian function that is built in to MMSP // Try using a grid of vectors >> one iterated with this code, one with laplacian diff --git a/examples/beginners_diffusion/MMSPDiffusion2D.cpp b/examples/beginners_diffusion/MMSPDiffusion2D.cpp index 0f2b421..48f6877 100644 --- a/examples/beginners_diffusion/MMSPDiffusion2D.cpp +++ b/examples/beginners_diffusion/MMSPDiffusion2D.cpp @@ -1,7 +1,7 @@ #include"MMSP.hpp" using namespace MMSP; -// Add in Laplacian funciton that is built in to MMSP +// Add in Laplacian function that is built in to MMSP // Try using a grid of vectors >> one iterated with this code, one with laplacian diff --git a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp index 6e1fe66..8dac8a7 100644 --- a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp +++ b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp @@ -17,36 +17,38 @@ namespace MMSP void generate(int dim, const char* filename) { if (dim==1) { - MMSP::grid<1,int> grid(0,0,128); + GRID1D initGrid(0,0,128); - for (int i=0; i x = position(grid,i); - if (x[0]<32) grid(i) = 3; - else if (x[0]>96) grid(i) = 2; - else grid(i) = 0; + for (int i=0; i x = position(initGrid,i); + if (x[0]<32) initGrid(i) = 3; + else if (x[0]>96) initGrid(i) = 2; + else initGrid(i) = 0; } - output(grid,filename); + output(initGrid,filename); } if (dim==2) { - MMSP::grid<2,int> grid(2,0,64,0,64); - for (int i=0; i grid(2,0,32,0,32,0,32); - for (int i=0; i bool OutsideDomainCheck(MMSP::grid& grid, vector* x){ +template bool OutsideDomainCheck(grid& mcGrid, vector* x){ bool outside_domain=false; for(int i=0; ix1(grid, i)){ + if((*x)[i]x1(mcGrid, i)){ outside_domain=true; break; } @@ -54,7 +56,7 @@ template bool OutsideDomainCheck(MMSP::grid& grid, vector void update(MMSP::grid& grid, int steps) +template void update(grid& mcGrid, int steps) { int rank=0; unsigned int np=0; @@ -68,8 +70,8 @@ template void update(MMSP::grid& grid, int steps) int dimension_length=0, number_of_lattice_cells=1; int lattice_cells_each_dimension[dim]; for(int i=0; i void update(MMSP::grid& grid, int steps) int coordinates_of_cell[dim]; int initial_coordinates[dim]; - int num_of_grids_to_flip[( static_cast(pow(2,dim)) )]; + int num_grids_to_flip[( static_cast(pow(2,dim)) )]; int first_cell_start_coordinates[dim]; - for(int kk=0; kk void update(MMSP::grid& grid, int steps) if(dim==2){ x_prim = x; - if(!OutsideDomainCheck(grid, &x_prim)) num_of_grids_to_flip[0]+=1; + if(!OutsideDomainCheck(mcGrid, &x_prim)) num_grids_to_flip[0]+=1; x_prim = x; x_prim[1]=x[1]+1; //0,1 - if(!OutsideDomainCheck(grid, &x_prim)) num_of_grids_to_flip[1]+=1; + if(!OutsideDomainCheck(mcGrid, &x_prim)) num_grids_to_flip[1]+=1; x_prim = x; x_prim[0]=x[0]+1; //1,0 - if(!OutsideDomainCheck(grid, &x_prim)) num_of_grids_to_flip[2]+=1; + if(!OutsideDomainCheck(mcGrid, &x_prim)) num_grids_to_flip[2]+=1; x_prim = x; x_prim[0]=x[0]+1; x_prim[1]=x[1]+1; //1,1 - if(!OutsideDomainCheck(grid, &x_prim)) num_of_grids_to_flip[3]+=1; + if(!OutsideDomainCheck(mcGrid, &x_prim)) num_grids_to_flip[3]+=1; }else if(dim==3){ x_prim = x;//0,0,0 - if(!OutsideDomainCheck(grid, &x_prim)) num_of_grids_to_flip[0]+=1; + if(!OutsideDomainCheck(mcGrid, &x_prim)) num_grids_to_flip[0]+=1; x_prim = x; x_prim[2]=x[2]+1; //0,0,1 - if(!OutsideDomainCheck(grid, &x_prim)) num_of_grids_to_flip[1]+=1; + if(!OutsideDomainCheck(mcGrid, &x_prim)) num_grids_to_flip[1]+=1; x_prim = x; x_prim[1]=x[1]+1; //0,1,0 - if(!OutsideDomainCheck(grid, &x_prim)) num_of_grids_to_flip[2]+=1; + if(!OutsideDomainCheck(mcGrid, &x_prim)) num_grids_to_flip[2]+=1; x_prim = x; x_prim[2]=x[2]+1; x_prim[1]=x[1]+1; //0,1,1 - if(!OutsideDomainCheck(grid, &x_prim)) num_of_grids_to_flip[3]+=1; + if(!OutsideDomainCheck(mcGrid, &x_prim)) num_grids_to_flip[3]+=1; x_prim = x; x_prim[0]=x[0]+1; //1,0,0 - if(!OutsideDomainCheck(grid, &x_prim)) num_of_grids_to_flip[4]+=1; + if(!OutsideDomainCheck(mcGrid, &x_prim)) num_grids_to_flip[4]+=1; x_prim = x; x_prim[2]=x[2]+1; x_prim[0]=x[0]+1; //1,0,1 - if(!OutsideDomainCheck(grid, &x_prim)) num_of_grids_to_flip[5]+=1; + if(!OutsideDomainCheck(mcGrid, &x_prim)) num_grids_to_flip[5]+=1; x_prim = x; x_prim[1]=x[1]+1; x_prim[0]=x[0]+1; //1,1,0 - if(!OutsideDomainCheck(grid, &x_prim)) num_of_grids_to_flip[6]+=1; + if(!OutsideDomainCheck(mcGrid, &x_prim)) num_grids_to_flip[6]+=1; x_prim = x; x_prim[2]=x[2]+1; x_prim[1]=x[1]+1; x_prim[0]=x[0]+1; //1,1,1 - if(!OutsideDomainCheck(grid, &x_prim)) num_of_grids_to_flip[7]+=1; + if(!OutsideDomainCheck(mcGrid, &x_prim)) num_grids_to_flip[7]+=1; } }// for int j for(int k=0; k x (dim,0); - for (int hh=0; hh void update(MMSP::grid& grid, int steps) bool site_out_of_domain = false; for(int i=0; i=x1(grid, i)){ -// if(x[i]x1(grid, i)-1){ + if(x[i]=x1(mcGrid, i)){ +// if(x[i]x1(mcGrid, i)-1){ site_out_of_domain = true; break;//break from the for int i loop } @@ -226,13 +228,13 @@ template void update(MMSP::grid& grid, int steps) double Q = 1.0e5; double R = 8.314; - double T = 673 + 50.0/(g1(grid, 0)-g0(grid, 0))*(x[0]-g0(grid, 0)); // temperature is 673K at one half and 723K at the other half + double T = 673 + 50.0/(g1(mcGrid, 0)-g0(mcGrid, 0))*(x[0]-g0(mcGrid, 0)); // temperature is 673K at one half and 723K at the other half double site_selection_probability = exp(-Q/R/T)/exp(-Q/R/723); double rd = double(rand())/double(RAND_MAX); if(rd>site_selection_probability) continue;//this site wont be selected - int spin1 = (grid)(x); + int spin1 = (mcGrid)(x); // determine neighboring spinsss vector r(dim,0); std::vector neighbors; @@ -244,9 +246,9 @@ template void update(MMSP::grid& grid, int steps) if(!(i==0 && j==0)){ r[0] = x[0] + i; r[1] = x[1] + j; - if(r[0]=g1(grid, 0) || r[1]=g1(grid, 1) )// not periodic BC + if(r[0]=g1(mcGrid, 0) || r[1]=g1(mcGrid, 1) )// not periodic BC continue;// neighbour outside the global boundary, skip it. - int spin = (grid)(r); + int spin = (mcGrid)(r); neighbors.push_back(spin); if(spin==spin1) number_of_same_neighours++; @@ -261,10 +263,10 @@ template void update(MMSP::grid& grid, int steps) r[0] = x[0] + i; r[1] = x[1] + j; r[2] = x[2] + k; - if(r[0]=g1(grid, 0) || r[1]=g1(grid, 1) || - r[2]=g1(grid, 2))// not periodic BC + if(r[0]=g1(mcGrid, 0) || r[1]=g1(mcGrid, 1) || + r[2]=g1(mcGrid, 2))// not periodic BC continue;// neighbour outside the global boundary, skip it. - int spin = (grid)(r); + int spin = (mcGrid)(r); neighbors.push_back(spin); if(spin==spin1) number_of_same_neighours++; @@ -290,9 +292,9 @@ template void update(MMSP::grid& grid, int steps) if(!(i==0 && j==0)){ r[0] = x[0] + i; r[1] = x[1] + j; - if(r[0]=g1(grid, 0) || r[1]=g1(grid, 1) ) // not periodic BC + if(r[0]=g1(mcGrid, 0) || r[1]=g1(mcGrid, 1) ) // not periodic BC continue;// neighbour outside the global boundary, skip it. - int spin = (grid)(r); + int spin = (mcGrid)(r); dE += 1.0/2*((spin!=spin2)-(spin!=spin1)); }// if(!(i==0 && j==0)) } @@ -306,10 +308,10 @@ template void update(MMSP::grid& grid, int steps) r[0] = x[0] + i; r[1] = x[1] + j; r[2] = x[2] + k; - if(r[0]=g1(grid, 0) || r[1]=g1(grid, 1) || - r[2]=g1(grid, 2)) // not periodic BC + if(r[0]=g1(mcGrid, 0) || r[1]=g1(mcGrid, 1) || + r[2]=g1(mcGrid, 2)) // not periodic BC continue;// neighbour outside the global boundary, skip it. - int spin = (grid)(r); + int spin = (mcGrid)(r); dE += 1.0/2*(spin!=spin2)-(spin!=spin1); } } @@ -319,15 +321,15 @@ template void update(MMSP::grid& grid, int steps) // attempt a spin flip double r = double(rand())/double(RAND_MAX); double kT = 1.3803288e-23*273; - if (dE <= 0.0) (grid)(x) = spin2; - else if (r > grid(4,0,128); + GRID1D initGrid(4,0,128); - for (int i=0; i x = position(grid,i); + vector x = position(initGrid,i); - if (x[0]<32) grid(i)[3] = 1.0; - else if (x[0]>96) grid(i)[3] = 1.0; - else grid(i)[0] = 1.0; + if (x[0]<32) initGrid(i)[3] = 1.0; + else if (x[0]>96) initGrid(i)[3] = 1.0; + else initGrid(i)[0] = 1.0; } - output(grid,filename); + output(initGrid,filename); } if (dim==2) { - MMSP::grid<2,vector > grid(4,0,128,0,128); + GRID2D initGrid(4,0,128,0,128); - for (int i=0; i x = position(grid,i); + vector x = position(initGrid,i); if (x[0]<32) { - if (x[1]<64) grid(i)[2] = 1.0; - else grid(i)[3] = 1.0; + if (x[1]<64) initGrid(i)[2] = 1.0; + else initGrid(i)[3] = 1.0; } else if (x[0]>96) { - if (x[1]<64) grid(i)[2] = 1.0; - else grid(i)[3] = 1.0; + if (x[1]<64) initGrid(i)[2] = 1.0; + else initGrid(i)[3] = 1.0; } else { - if (x[1]<32 or x[1]>96) grid(i)[1] = 1.0; - else grid(i)[0] = 1.0; + if (x[1]<32 or x[1]>96) initGrid(i)[1] = 1.0; + else initGrid(i)[0] = 1.0; } } - output(grid,filename); + output(initGrid,filename); } if (dim==3) { - MMSP::grid<2,vector > grid(4,0,128,0,128); + GRID3D initGrid(4,0,128,0,128); - for (int i=0; i x = position(grid,i); + vector x = position(initGrid,i); if (x[0]<16) { - if (x[1]<32) grid(i)[2] = 1.0; - else grid(i)[3] = 1.0; + if (x[1]<32) initGrid(i)[2] = 1.0; + else initGrid(i)[3] = 1.0; } else if (x[0]>48) { - if (x[1]<32) grid(i)[2] = 1.0; - else grid(i)[3] = 1.0; + if (x[1]<32) initGrid(i)[2] = 1.0; + else initGrid(i)[3] = 1.0; } else { - if (x[1]<16 or x[1]>48) grid(i)[1] = 1.0; - else grid(i)[0] = 1.0; + if (x[1]<16 or x[1]>48) initGrid(i)[1] = 1.0; + else initGrid(i)[0] = 1.0; } } - output(grid,filename); + output(initGrid,filename); } } -template void update(MMSP::grid >& grid, int steps) +template void update(grid >& oldGrid, int steps) { - MMSP::grid > update(grid); + grid > newGrid(oldGrid); double dt = 0.01; double width = 8.0; for (int step=0; step x = position(grid,i); + for (int i=0; i x = position(oldGrid,i); - // determine nonzero fields within + // determine nonzero fields within // the neighborhood of this node - double S = 0.0; - vector s(fields(grid),0); - for (int h=0; h s(fields(oldGrid),0); + for (int h=0; h0.0) { + if (oldGrid(x)[h]>0.0) { s[h] = 1; x[j] -= k; goto next; @@ -113,62 +113,62 @@ template void update(MMSP::grid >& grid, int steps) } // if only one field is nonzero, - // then copy this node to update - if (S<2.0) update(i) = grid(i); + // then copy this node to newGrid + if (S<2.0) newGrid(i) = oldGrid(i); else { // compute laplacian of each field - vector lap = laplacian(grid,i); + vector lap = laplacian(oldGrid,i); // compute variational derivatives - vector dFdp(fields(grid),0.0); - for (int h=0; h dFdp(fields(oldGrid),0.0); + for (int h=0; h0.0) - for (int j=h+1; j0.0) { double gamma = energy(h,j); double eps = 4.0/acos(-1.0)*sqrt(0.5*gamma*width); double w = 4.0*gamma/width; - dFdp[h] += 0.5*eps*eps*lap[j]+w*grid(i)[j]; - dFdp[j] += 0.5*eps*eps*lap[h]+w*grid(i)[h]; - for (int k=j+1; k0.0) { - dFdp[h] += 3.0*grid(i)[j]*grid(i)[k]; - dFdp[j] += 3.0*grid(i)[k]*grid(i)[h]; - dFdp[k] += 3.0*grid(i)[h]*grid(i)[j]; + dFdp[h] += 3.0*oldGrid(i)[j]*oldGrid(i)[k]; + dFdp[j] += 3.0*oldGrid(i)[k]*oldGrid(i)[h]; + dFdp[k] += 3.0*oldGrid(i)[h]*oldGrid(i)[j]; } } // compute time derivatives - vector dpdt(fields(grid),0.0); - for (int h=0; h dpdt(fields(oldGrid),0.0); + for (int h=0; h0.0) - for (int j=h+1; j0.0) { double mu = mobility(h,j); dpdt[h] -= mu*(dFdp[h]-dFdp[j]); dpdt[j] -= mu*(dFdp[j]-dFdp[h]); } - // compute update values - double sum = 0.0; - for (int h=0; h1.0) value = 1.0; if (value<0.0) value = 0.0; - update(i)[h] = value; + newGrid(i)[h] = value; sum += value; } // project onto Gibbs simplex - double rsum = 0.0; + T rsum = 0.0; if (fabs(sum)>0.0) rsum = 1.0/sum; - for (int h=0; h > grid(0,0,128); + GRID1D initGrid(0,0,128); - for (int i=0; i x = position(grid,i); + for (int i=0; i x = position(initGrid,i); - if (x[0]<32) set(grid(i),3) = 1.0; - else if (x[0]>96) set(grid(i),3) = 1.0; - else set(grid(i),0) = 1.0; + if (x[0]<32) set(initGrid(i),3) = 1.0; + else if (x[0]>96) set(initGrid(i),3) = 1.0; + else set(initGrid(i),0) = 1.0; } - output(grid,filename); + output(initGrid,filename); } if (dim==2) { - MMSP::grid<2,sparse > grid(0,0,128,0,128); + GRID2D initGrid(0,0,128,0,128); - for (int i=0; i x = position(grid,i); + for (int i=0; i x = position(initGrid,i); if (x[0]<32) { - if (x[1]<64) set(grid(i),2) = 1.0; - else set(grid(i),3) = 1.0; + if (x[1]<64) set(initGrid(i),2) = 1.0; + else set(initGrid(i),3) = 1.0; } else if (x[0]>96) { - if (x[1]<64) set(grid(i),2) = 1.0; - else set(grid(i),3) = 1.0; + if (x[1]<64) set(initGrid(i),2) = 1.0; + else set(initGrid(i),3) = 1.0; } else { - if (x[1]<32 or x[1]>96) set(grid(i),1) = 1.0; - else set(grid(i),0) = 1.0; + if (x[1]<32 or x[1]>96) set(initGrid(i),1) = 1.0; + else set(initGrid(i),0) = 1.0; } } - output(grid,filename); + output(initGrid,filename); } if (dim==3) { - MMSP::grid<3,sparse > grid(0,0,64,0,64,0,64); + GRID3D initGrid(0,0,64,0,64,0,64); - for (int i=0; i x = position(grid,i); + for (int i=0; i x = position(initGrid,i); if (x[0]<16) { - if (x[1]<32) set(grid(i),2) = 1.0; - else set(grid(i),3) = 1.0; + if (x[1]<32) set(initGrid(i),2) = 1.0; + else set(initGrid(i),3) = 1.0; } else if (x[0]>48) { - if (x[1]<32) set(grid(i),2) = 1.0; - else set(grid(i),3) = 1.0; + if (x[1]<32) set(initGrid(i),2) = 1.0; + else set(initGrid(i),3) = 1.0; } else { - if (x[1]<16 or x[1]>48) set(grid(i),1) = 1.0; - else set(grid(i),0) = 1.0; + if (x[1]<16 or x[1]>48) set(initGrid(i),1) = 1.0; + else set(initGrid(i),0) = 1.0; } } - output(grid,filename); + output(initGrid,filename); } } -template void update(MMSP::grid >& grid, int steps) +template void update(grid >& oldGrid, int steps) { double dt = 0.01; double width = 8.0; double epsilon = 1.0e-8; for (int step=0; step > update(grid); + // newGrid grid must be overwritten each time + grid > newGrid(oldGrid); - for (int i=0; i x = position(grid,i); + for (int n=0; n x = position(oldGrid,n); // determine nonzero fields within // the neighborhood of this node @@ -93,76 +93,76 @@ template void update(MMSP::grid >& grid, int steps) for (int j=0; j lap = laplacian(grid,i); + sparse lap = laplacian(oldGrid,n); // compute variational derivatives - sparse dFdp; + sparse dFdp; for (int h=0; h dpdt; + sparse dpdt; for (int h=0; h1.0) value = 1.0; if (value<0.0) value = 0.0; - if (value>epsilon) set(update(i),index) = value; - sum += update(i)[index]; + if (value>epsilon) set(newGrid(n),i) = value; + sum += newGrid(n)[i]; } // project onto Gibbs simplex double rsum = 0.0; if (fabs(sum)>0.0) rsum = 1.0/sum; - for (int h=0; h grid(0,0,128); + GRID1D initGrid(0,0,128); - for (int i=0; i grid(2,0,64,0,64); - for (int i=0; i grid(2,0,32,0,32,0,32); - for (int i=0; i bool OutsideDomainCheck(MMSP::grid& grid, vector* x){ +template bool OutsideDomainCheck(grid& mcGrid, vector* x){ bool outside_domain=false; for(int i=0; ix1(grid, i)){ + if((*x)[i]x1(mcGrid, i)){ outside_domain=true; break; } @@ -49,7 +51,7 @@ template bool OutsideDomainCheck(MMSP::grid& grid, vector void update(MMSP::grid& grid, int steps) +template void update(grid& mcGrid, int steps) { int rank=0; unsigned int np=0; @@ -63,8 +65,8 @@ template void update(MMSP::grid& grid, int steps) int dimension_length=0, number_of_lattice_cells=1; int lattice_cells_each_dimension[dim]; for(int i=0; i void update(MMSP::grid& grid, int steps) int num_of_grids_to_flip[( static_cast(pow(2,dim)) )]; int first_cell_start_coordinates[dim]; - for(int kk=0; kk void update(MMSP::grid& grid, int steps) if(dim==2){ x_prim = x; - if(!OutsideDomainCheck(grid, &x_prim)) num_of_grids_to_flip[0]+=1; + if(!OutsideDomainCheck(mcGrid, &x_prim)) num_of_grids_to_flip[0]+=1; x_prim = x; x_prim[1]=x[1]+1; //0,1 - if(!OutsideDomainCheck(grid, &x_prim)) num_of_grids_to_flip[1]+=1; + if(!OutsideDomainCheck(mcGrid, &x_prim)) num_of_grids_to_flip[1]+=1; x_prim = x; x_prim[0]=x[0]+1; //1,0 - if(!OutsideDomainCheck(grid, &x_prim)) num_of_grids_to_flip[2]+=1; + if(!OutsideDomainCheck(mcGrid, &x_prim)) num_of_grids_to_flip[2]+=1; x_prim = x; x_prim[0]=x[0]+1; x_prim[1]=x[1]+1; //1,1 - if(!OutsideDomainCheck(grid, &x_prim)) num_of_grids_to_flip[3]+=1; + if(!OutsideDomainCheck(mcGrid, &x_prim)) num_of_grids_to_flip[3]+=1; }else if(dim==3){ x_prim = x;//0,0,0 - if(!OutsideDomainCheck(grid, &x_prim)) num_of_grids_to_flip[0]+=1; + if(!OutsideDomainCheck(mcGrid, &x_prim)) num_of_grids_to_flip[0]+=1; x_prim = x; x_prim[2]=x[2]+1; //0,0,1 - if(!OutsideDomainCheck(grid, &x_prim)) num_of_grids_to_flip[1]+=1; + if(!OutsideDomainCheck(mcGrid, &x_prim)) num_of_grids_to_flip[1]+=1; x_prim = x; x_prim[1]=x[1]+1; //0,1,0 - if(!OutsideDomainCheck(grid, &x_prim)) num_of_grids_to_flip[2]+=1; + if(!OutsideDomainCheck(mcGrid, &x_prim)) num_of_grids_to_flip[2]+=1; x_prim = x; x_prim[2]=x[2]+1; x_prim[1]=x[1]+1; //0,1,1 - if(!OutsideDomainCheck(grid, &x_prim)) num_of_grids_to_flip[3]+=1; + if(!OutsideDomainCheck(mcGrid, &x_prim)) num_of_grids_to_flip[3]+=1; x_prim = x; x_prim[0]=x[0]+1; //1,0,0 - if(!OutsideDomainCheck(grid, &x_prim)) num_of_grids_to_flip[4]+=1; + if(!OutsideDomainCheck(mcGrid, &x_prim)) num_of_grids_to_flip[4]+=1; x_prim = x; x_prim[2]=x[2]+1; x_prim[0]=x[0]+1; //1,0,1 - if(!OutsideDomainCheck(grid, &x_prim)) num_of_grids_to_flip[5]+=1; + if(!OutsideDomainCheck(mcGrid, &x_prim)) num_of_grids_to_flip[5]+=1; x_prim = x; x_prim[1]=x[1]+1; x_prim[0]=x[0]+1; //1,1,0 - if(!OutsideDomainCheck(grid, &x_prim)) num_of_grids_to_flip[6]+=1; + if(!OutsideDomainCheck(mcGrid, &x_prim)) num_of_grids_to_flip[6]+=1; x_prim = x; x_prim[2]=x[2]+1; x_prim[1]=x[1]+1; x_prim[0]=x[0]+1; //1,1,1 - if(!OutsideDomainCheck(grid, &x_prim)) num_of_grids_to_flip[7]+=1; + if(!OutsideDomainCheck(mcGrid, &x_prim)) num_of_grids_to_flip[7]+=1; } }// for int j for(int k=0; k void update(MMSP::grid& grid, int steps) bool site_out_of_domain = false; for(int i=0; i=x1(grid, i)){ -// if(x[i]x1(grid, i)-1){ + if(x[i]=x1(mcGrid, i)){ +// if(x[i]x1(mcGrid, i)-1){ site_out_of_domain = true; break;//break from the for int i loop } @@ -224,7 +226,7 @@ template void update(MMSP::grid& grid, int steps) double rd = double(rand())/double(RAND_MAX); if(rd>site_selection_probability) continue;//this site wont be selected - int spin1 = (grid)(x); + int spin1 = (mcGrid)(x); // determine neighboring spinsss vector r(dim,0); std::vector neighbors; @@ -236,9 +238,9 @@ template void update(MMSP::grid& grid, int steps) if(!(i==0 && j==0)){ r[0] = x[0] + i; r[1] = x[1] + j; - if(r[0]=g1(grid, 0) || r[1]=g1(grid, 1) )// not periodic BC + if(r[0]=g1(mcGrid, 0) || r[1]=g1(mcGrid, 1) )// not periodic BC continue;// neighbour outside the global boundary, skip it. - int spin = (grid)(r); + int spin = (mcGrid)(r); neighbors.push_back(spin); if(spin==spin1) number_of_same_neighours++; @@ -253,10 +255,10 @@ template void update(MMSP::grid& grid, int steps) r[0] = x[0] + i; r[1] = x[1] + j; r[2] = x[2] + k; - if(r[0]=g1(grid, 0) || r[1]=g1(grid, 1) || - r[2]=g1(grid, 2))// not periodic BC + if(r[0]=g1(mcGrid, 0) || r[1]=g1(mcGrid, 1) || + r[2]=g1(mcGrid, 2))// not periodic BC continue;// neighbour outside the global boundary, skip it. - int spin = (grid)(r); + int spin = (mcGrid)(r); neighbors.push_back(spin); if(spin==spin1) number_of_same_neighours++; @@ -282,9 +284,9 @@ template void update(MMSP::grid& grid, int steps) if(!(i==0 && j==0)){ r[0] = x[0] + i; r[1] = x[1] + j; - if(r[0]=g1(grid, 0) || r[1]=g1(grid, 1) ) // not periodic BC + if(r[0]=g1(mcGrid, 0) || r[1]=g1(mcGrid, 1) ) // not periodic BC continue;// neighbour outside the global boundary, skip it. - int spin = (grid)(r); + int spin = (mcGrid)(r); dE += 1.0/2*((spin!=spin2)-(spin!=spin1)); }// if(!(i==0 && j==0)) } @@ -298,10 +300,10 @@ template void update(MMSP::grid& grid, int steps) r[0] = x[0] + i; r[1] = x[1] + j; r[2] = x[2] + k; - if(r[0]=g1(grid, 0) || r[1]=g1(grid, 1) || - r[2]=g1(grid, 2)) // not periodic BC + if(r[0]=g1(mcGrid, 0) || r[1]=g1(mcGrid, 1) || + r[2]=g1(mcGrid, 2)) // not periodic BC continue;// neighbour outside the global boundary, skip it. - int spin = (grid)(r); + int spin = (mcGrid)(r); dE += 1.0/2*(spin!=spin2)-(spin!=spin1); } } @@ -311,15 +313,15 @@ template void update(MMSP::grid& grid, int steps) // attempt a spin flip double r = double(rand())/double(RAND_MAX); double kT = 1.3803288e-23*273; - if (dE <= 0.0) (grid)(x) = spin2; - else if (r > grid(2,0,128); + GRID1D initGrid(2,0,128); - for (int i=0; i x = position(grid,i); - grid(x)[0] = 0.0; - grid(x)[1] = 0.0; + for (int i=0; i x = position(initGrid,i); + initGrid(x)[0] = 0.0; + initGrid(x)[1] = 0.0; double d = 64.0-x[0]; - if (d<32.0) grid(x)[1] = 1.0; - else grid(x)[0] = 1.0; + if (d<32.0) initGrid(x)[1] = 1.0; + else initGrid(x)[0] = 1.0; } - output(grid,filename); + output(initGrid,filename); } if (dim==2) { - MMSP::grid<2,vector > grid(2,0,128,0,128); + GRID2D initGrid(2,0,128,0,128); - for (int i=0; i x = position(grid,i); - grid(x)[0] = 0.0; - grid(x)[1] = 0.0; + for (int i=0; i x = position(initGrid,i); + initGrid(x)[0] = 0.0; + initGrid(x)[1] = 0.0; double d = sqrt(pow(64.0-x[0],2)+pow(64.0-x[1],2)); - if (d<32.0) grid(x)[1] = 1.0; - else grid(x)[0] = 1.0; + if (d<32.0) initGrid(x)[1] = 1.0; + else initGrid(x)[0] = 1.0; } - output(grid,filename); + output(initGrid,filename); } if (dim==3) { - MMSP::grid<3,vector > grid(2,0,64,0,64,0,64); + GRID3D initGrid(2,0,64,0,64,0,64); - for (int i=0; i x = position(grid,i); - grid(x)[0] = 0.0; - grid(x)[1] = 0.0; + for (int i=0; i x = position(initGrid,i); + initGrid(x)[0] = 0.0; + initGrid(x)[1] = 0.0; double d = sqrt(pow(32.0-x[0],2)+pow(32.0-x[1],2)+pow(32.0-x[2],2)); - if (d<16.0) grid(x)[1] = 1.0; - else grid(x)[0] = 1.0; + if (d<16.0) initGrid(x)[1] = 1.0; + else initGrid(x)[0] = 1.0; } - output(grid,filename); + output(initGrid,filename); } } -template void update(MMSP::grid >& grid, int steps) +template void update(grid >& oldGrid, int steps) { - MMSP::grid > update(grid); + grid > newGrid(oldGrid); double dt = 0.01; for (int step=0; step lap = laplacian(grid,i); + vector lap = laplacian(oldGrid,i); // compute sum of squares double sum = 0.0; - for (int j=0; j > grid(0,0,128); + GRID1D initGrid(0,0,128); - for (int i=0; i x = position(grid,i); + for (int i=0; i x = position(initGrid,i); double d = 64.0-x[0]; - if (d<32.0) set(grid(i),1)= 1.0; - else set(grid(i),0) = 1.0; + if (d<32.0) set(initGrid(i),1)= 1.0; + else set(initGrid(i),0) = 1.0; } - output(grid,filename); + output(initGrid,filename); } if (dim==2) { - MMSP::grid<2,sparse > grid(0,0,128,0,128); + GRID2D initGrid(0,0,128,0,128); - for (int i=0; i x = position(grid,i); + for (int i=0; i x = position(initGrid,i); double d = sqrt(pow(64.0-x[0],2)+pow(64.0-x[1],2)); - if (d<32.0) set(grid(i),1)= 1.0; - else set(grid(i),0) = 1.0; + if (d<32.0) set(initGrid(i),1)= 1.0; + else set(initGrid(i),0) = 1.0; } - output(grid,filename); + output(initGrid,filename); } if (dim==3) { - MMSP::grid<3,sparse > grid(0,0,64,0,64,0,64); + GRID3D initGrid(0,0,64,0,64,0,64); - for (int i=0; i x = position(grid,i); + for (int i=0; i x = position(initGrid,i); double d = sqrt(pow(32.0-x[0],2)+pow(32.0-x[1],2)+pow(32.0-x[2],2)); - if (d<16.0) set(grid(i),1)= 1.0; - else set(grid(i),0) = 1.0; + if (d<16.0) set(initGrid(i),1)= 1.0; + else set(initGrid(i),0) = 1.0; } - output(grid,filename); + output(initGrid,filename); } } -template void update(MMSP::grid >& grid, int steps) +template void update(grid >& oldGrid, int steps) { double dt = 0.01; double epsilon = 1.0e-8; for (int step=0; step > update(grid); + grid > newGrid(oldGrid); - for (int i=0; i x = position(grid,i); + for (int n=0; n x = position(oldGrid,n); // determine nonzero fields within // the neighborhood of this node @@ -70,9 +70,9 @@ template void update(MMSP::grid >& grid, int steps) for (int j=0; j void update(MMSP::grid >& grid, int steps) // if there is only one nonzero // field, it remains the same if (length(neighbors)<2) - update(i) = grid(i); + newGrid(n) = oldGrid(n); else { // compute laplacian - sparse lap = laplacian(grid,i); + sparse lap = laplacian(oldGrid,n); // compute sum of squares double sum = 0.0; - for (int j=0; jepsilon) set(update(i),index) = value; + int i = index(neighbors,j); + T phi = oldGrid(n)[i]; + T value = phi-dt*(-phi-pow(phi,3)+2.0*(phi*sum-lap[i])); + if (value>epsilon) set(newGrid(n),i) = value; } } } - swap(grid,update); - ghostswap(grid); + swap(oldGrid,newGrid); + ghostswap(oldGrid); } } diff --git a/examples/coarsening/ostwald_ripening/isotropic/phase_field/ostwald.cpp b/examples/coarsening/ostwald_ripening/isotropic/phase_field/ostwald.cpp index 6deca0c..0ceeffe 100644 --- a/examples/coarsening/ostwald_ripening/isotropic/phase_field/ostwald.cpp +++ b/examples/coarsening/ostwald_ripening/isotropic/phase_field/ostwald.cpp @@ -13,62 +13,64 @@ namespace MMSP{ void generate(int dim, const char* filename) { if (dim==1) { - MMSP::grid<1,MMSP::vector > grid(2,0,128); + GRID1D initGrid(2,0,128); - int x0 = MMSP::x0(grid); - int x1 = MMSP::x1(grid); + int x0 = x0(initGrid); + int x1 = x1(initGrid); for (int x=x0; x > grid(2,0,128,0,128); - int x0 = MMSP::x0(grid); - int x1 = MMSP::x1(grid); - int y0 = MMSP::y0(grid); - int y1 = MMSP::y1(grid); + GRID2D initGrid(2,0,128,0,128); + + int x0 = x0(initGrid); + int x1 = x1(initGrid); + int y0 = y0(initGrid); + int y1 = y1(initGrid); for (int x=x0; x > grid(2,0,64,0,64,0,64); - int x0 = MMSP::x0(grid); - int x1 = MMSP::x1(grid); - int y0 = MMSP::y0(grid); - int y1 = MMSP::y1(grid); - int z0 = MMSP::z0(grid); - int z1 = MMSP::z1(grid); + GRID3D initGrid(2,0,64,0,64,0,64); + + int x0 = x0(initGrid); + int x1 = x1(initGrid); + int y0 = y0(initGrid); + int y1 = y1(initGrid); + int z0 = z0(initGrid); + int z1 = z1(initGrid); for (int x=x0; x >& grid, int steps) +template void update(grid >& oldGrid, int steps) { - MMSP::grid<2,MMSP::vector > update(grid); - MMSP::grid<2,double> wspace(grid,1); + grid > newGrid(oldGrid); + grid wspace(oldGrid,1); double dt = 0.01; double L = 1.0; @@ -88,127 +90,41 @@ void update(MMSP::grid<2,MMSP::vector >& grid, int steps) for (int step=0; step x = position(oldGrid, n); + T lap = laplacian(wspace,x); + T C = oldGrid(n)[0]; - double sum = 0.0; - for (int i=1; i vlap = laplacian(oldGrid,x); + for (int i=1; i >& grid, int steps) -{ - MMSP::grid<3,MMSP::vector > update(grid); - MMSP::grid<3,double> wspace(grid,1); - - double dt = 0.01; - double L = 1.0; - double D = 1.0; - - double Calpha = 0.05; - double Cbeta = 0.95; - double Cmatrix = 0.5*(Calpha+Cbeta); - double A = 2.0; - double B = A/pow(Cbeta-Cmatrix,2); - double gamma = 2.0/pow(Cbeta-Calpha,2); - double delta = 1.0; - double epsilon = 3.0; - double Dalpha = gamma/pow(delta,2); - double Dbeta = gamma/pow(delta,2); - double kappa = 2.0; - - - for (int step=0; step initGrid(0,0,128); + GRID1D initGrid(0,0,128); for (int i=0; i x = position(initGrid,i); @@ -34,7 +34,7 @@ void generate(int dim, const char* filename) } if (dim==2) { - grid<2,int> initGrid(0,0,128,0,128); + GRID2D initGrid(0,0,128,0,128); for (int i=0; i x = position(initGrid,i); @@ -55,7 +55,7 @@ void generate(int dim, const char* filename) } if (dim==3) { - grid<3,int> initGrid(0,0,64,0,64,0,64); + GRID3D initGrid(0,0,64,0,64,0,64); for (int i=0; i x = position(initGrid,i); @@ -77,9 +77,9 @@ void generate(int dim, const char* filename) } } -void update(grid<1,int>& mcGrid, int steps) +template void update(grid& mcGrid, int steps) { - const double kT = 0.50; + const double kT = (dim==3)?0.75:0.50; int gss = int(nodes(mcGrid)); for (int step=0; step& mcGrid, int steps) if (spin1!=0) { // determine neighboring spins sparse neighbors; - for (int i=-1; i<=1; i++) { - int spin = mcGrid[x[0]+i]; - set(neighbors,spin) = true; - } - - // choose a random neighbor spin - int spin2 = index(neighbors,rand()%length(neighbors)); - - if (spin1!=spin2 and spin2!=0) { - // compute energy change - double dE = -energy(spin1,spin2); + if (dim==1) { for (int i=-1; i<=1; i++) { int spin = mcGrid[x[0]+i]; - dE += energy(spin,spin2)-energy(spin,spin1); + set(neighbors,spin) = true; } - - // compute boundary energy, mobility - double E = energy(spin1,spin2); - double M = mobility(spin1,spin2); - - // attempt a spin flip - double r = double(rand())/double(RAND_MAX); - if (dE<=0.0 and r0.0 and r& mcGrid, int steps) -{ - const double kT = 0.50; - int gss = int(sqrt(nodes(mcGrid))); - - for (int step=0; step x = position(mcGrid,p); - int spin1 = mcGrid(p); - - if (spin1!=0) { - // determine neighboring spins - sparse neighbors; + } else if (dim==2) { for (int i=-1; i<=1; i++) - for (int j=-1; j<=1; j++) { - int spin = mcGrid[x[0]+i][x[1]+j]; - set(neighbors,spin) = true; - } - - // choose a random neighbor spin - int spin2 = index(neighbors,rand()%length(neighbors)); - - if (spin1!=spin2 and spin2!=0) { - // compute energy change - double dE = -energy(spin1,spin2); - for (int i=-1; i<=1; i++) - for (int j=-1; j<=1; j++){ + for (int j=-1; j<=1; j++) { int spin = mcGrid[x[0]+i][x[1]+j]; - dE += energy(spin,spin2)-energy(spin,spin1); - } - - // compute boundary energy, mobility - double E = energy(spin1,spin2); - double M = mobility(spin1,spin2); - - // attempt a spin flip - double r = double(rand())/double(RAND_MAX); - if (dE<=0.0 and r0.0 and r& mcGrid, int steps) -{ - const double kT = 0.75; - int gss = int(sqrt(nodes(mcGrid))); - - for (int step=0; step x = position(mcGrid,p); - int spin1 = mcGrid(p); - - if (spin1!=0) { - // determine neighboring spins - sparse neighbors; - for (int i=-1; i<=1; i++) - for (int j=-1; j<=1; j++) - for (int k=-1; k<=1; k++) { - int spin = mcGrid[x[0]+i][x[1]+j][x[2]+k]; set(neighbors,spin) = true; } + } else if (dim==3) { + for (int i=-1; i<=1; i++) + for (int j=-1; j<=1; j++) + for (int k=-1; k<=1; k++) { + int spin = mcGrid[x[0]+i][x[1]+j][x[2]+k]; + set(neighbors,spin) = true; + } + } // choose a random neighbor spin int spin2 = index(neighbors,rand()%length(neighbors)); @@ -199,12 +118,25 @@ void update(grid<3,int>& mcGrid, int steps) if (spin1!=spin2 and spin2!=0) { // compute energy change double dE = -energy(spin1,spin2); - for (int i=-1; i<=1; i++) - for (int j=-1; j<=1; j++) - for (int k=-1; k<=1; k++) { - int spin = mcGrid[x[0]+i][x[1]+j][x[2]+k]; + if (dim==1) { + for (int i=-1; i<=1; i++) { + int spin = mcGrid[x[0]+i]; dE += energy(spin,spin2)-energy(spin,spin1); } + } else if (dim==2) { + for (int i=-1; i<=1; i++) + for (int j=-1; j<=1; j++){ + int spin = mcGrid[x[0]+i][x[1]+j]; + dE += energy(spin,spin2)-energy(spin,spin1); + } + } else if (dim==3) { + for (int i=-1; i<=1; i++) + for (int j=-1; j<=1; j++) + for (int k=-1; k<=1; k++) { + int spin = mcGrid[x[0]+i][x[1]+j][x[2]+k]; + dE += energy(spin,spin2)-energy(spin,spin1); + } + } // compute boundary energy, mobility double E = energy(spin1,spin2); diff --git a/examples/coarsening/zener_pinning/anisotropic/phase_field/zener.cpp b/examples/coarsening/zener_pinning/anisotropic/phase_field/zener.cpp index d1dbd0c..88ad115 100644 --- a/examples/coarsening/zener_pinning/anisotropic/phase_field/zener.cpp +++ b/examples/coarsening/zener_pinning/anisotropic/phase_field/zener.cpp @@ -14,7 +14,7 @@ namespace MMSP{ void generate(int dim, const char* filename) { if (dim==1) { - grid<1,vector > initGrid(3,0,128); + GRID1D initGrid(3,0,128); for (int i=0; i x = position(initGrid,i); @@ -39,7 +39,7 @@ void generate(int dim, const char* filename) } if (dim==2) { - grid<2,vector > initGrid(3,0,128,0,128); + GRID2D initGrid(3,0,128,0,128); for (int i=0; i x = position(initGrid,i); @@ -65,7 +65,7 @@ void generate(int dim, const char* filename) } if (dim==3) { - grid<3,vector > initGrid(3,0,64,0,64,0,64); + GRID3D initGrid(3,0,64,0,64,0,64); for (int i=0; i x = position(initGrid,i); @@ -92,9 +92,9 @@ void generate(int dim, const char* filename) } } -template void update(grid >& oldGrid, int steps) +template void update(grid >& oldGrid, int steps) { - grid > newGrid(oldGrid); + grid > newGrid(oldGrid); double dt = 0.01; double width = 8.0; @@ -127,10 +127,10 @@ template void update(grid >& oldGrid, int steps) else { // compute laplacian of each field - vector lap = laplacian(oldGrid,i); + vector lap = laplacian(oldGrid,i); // compute variational derivatives - vector dFdp(fields(oldGrid),0.0); + vector dFdp(fields(oldGrid),0.0); for (int h=0; h0.0) for (int j=h+1; j void update(grid >& oldGrid, int steps) } // compute time derivatives - vector dpdt(fields(oldGrid),0.0); + vector dpdt(fields(oldGrid),0.0); for (int h=0; h0.0) for (int j=h+1; j void update(grid >& oldGrid, int steps) // compute newGrid values double sum = 0.0; for (int h=0; h1.0) value = 1.0; if (value<0.0) value = 0.0; newGrid(i)[h] = value; diff --git a/examples/coarsening/zener_pinning/anisotropic/sparsePF/zener.cpp b/examples/coarsening/zener_pinning/anisotropic/sparsePF/zener.cpp index 4202d84..5b931b1 100644 --- a/examples/coarsening/zener_pinning/anisotropic/sparsePF/zener.cpp +++ b/examples/coarsening/zener_pinning/anisotropic/sparsePF/zener.cpp @@ -14,7 +14,7 @@ namespace MMSP{ void generate(int dim, const char* filename) { if (dim==1) { - grid<1,sparse > initGrid(0,0,128); + GRID1D initGrid(0,0,128); for (int i=0; i x = position(initGrid,i); @@ -37,7 +37,7 @@ void generate(int dim, const char* filename) } if (dim==2) { - grid<2,sparse > initGrid(0,0,128,0,128); + GRID2D initGrid(0,0,128,0,128); for (int i=0; i x = position(initGrid,i); @@ -61,7 +61,7 @@ void generate(int dim, const char* filename) } if (dim==3) { - grid<3,sparse > initGrid(0,0,64,0,64,0,64); + GRID3D initGrid(0,0,64,0,64,0,64); for (int i=0; i x = position(initGrid,i); @@ -86,14 +86,14 @@ void generate(int dim, const char* filename) } } -template void update(grid >& oldGrid, int steps) +template void update(grid >& oldGrid, int steps) { double dt = 0.01; double width = 8.0; double epsilon = 1.0e-8; for (int step=0; step > newGrid(oldGrid); + grid > newGrid(oldGrid); for (int n=0; n void update(grid >& oldGrid, int steps) else { // compute laplacian of each field - sparse lap = laplacian(oldGrid,n); + sparse lap = laplacian(oldGrid,n); // compute variational derivatives - sparse dFdp; + sparse dFdp; for (int h=0; h void update(grid >& oldGrid, int steps) } // compute time derivatives - sparse dpdt; + sparse dpdt; for (int h=0; h void update(grid >& oldGrid, int steps) double sum = 0.0; for (int h=0; h1.0) value = 1.0; if (value<0.0) value = 0.0; if (value>epsilon) set(newGrid(n),i) = value; diff --git a/examples/coarsening/zener_pinning/isotropic/phase_field/zener.cpp b/examples/coarsening/zener_pinning/isotropic/phase_field/zener.cpp index b22c08d..27a0731 100644 --- a/examples/coarsening/zener_pinning/isotropic/phase_field/zener.cpp +++ b/examples/coarsening/zener_pinning/isotropic/phase_field/zener.cpp @@ -13,7 +13,7 @@ namespace MMSP{ void generate(int dim, const char* filename) { if (dim==1) { - grid<1,vector > initGrid(3,0,128); + GRID1D initGrid(3,0,128); for (int i=0; i x = position(initGrid,i); @@ -38,7 +38,7 @@ void generate(int dim, const char* filename) } if (dim==2) { - grid<2,vector > initGrid(3,0,128,0,128); + GRID2D initGrid(3,0,128,0,128); for (int i=0; i x = position(initGrid,i); @@ -64,7 +64,7 @@ void generate(int dim, const char* filename) } if (dim==3) { - grid<3,vector > initGrid(3,0,64,0,64,0,64); + GRID3D initGrid(3,0,64,0,64,0,64); for (int i=0; i x = position(initGrid,i); @@ -91,27 +91,27 @@ void generate(int dim, const char* filename) } } -template void update(grid >& oldGrid, int steps) +template void update(grid >& oldGrid, int steps) { - grid > newGrid(oldGrid); + grid > newGrid(oldGrid); double dt = 0.01; for (int step=0; step lap = laplacian(oldGrid,i); + vector lap = laplacian(oldGrid,i); // compute sums of squares double sum = 0.0; for (int j=0; j > initGrid(0,0,128); + GRID1D initGrid(0,0,128); for (int i=0; i x = position(initGrid,i); @@ -36,7 +36,7 @@ void generate(int dim, const char* filename) } if (dim==2) { - grid<2,sparse > initGrid(0,0,128,0,128); + GRID2D initGrid(0,0,128,0,128); for (int i=0; i x = position(initGrid,i); @@ -60,7 +60,7 @@ void generate(int dim, const char* filename) } if (dim==3) { - grid<3,sparse > initGrid(0,0,64,0,64,0,64); + GRID3D initGrid(0,0,64,0,64,0,64); for (int i=0; i x = position(initGrid,i); @@ -85,14 +85,14 @@ void generate(int dim, const char* filename) } } -template void update(grid >& oldGrid, int steps) +template void update(grid >& oldGrid, int steps) { double dt = 0.01; double epsilon = 1.0e-8; for (int step=0; step > newGrid(oldGrid); + grid > newGrid(oldGrid); for (int n=0; n x = position(oldGrid,n); @@ -117,23 +117,23 @@ template void update(grid >& oldGrid, int steps) else { // compute laplacians - sparse lap = laplacian(oldGrid,n); + sparse lap = laplacian(oldGrid,n); // compute sums of squares double sum = 0.0; for (int j=0; jepsilon) set(newGrid(n),i) = value; } } diff --git a/examples/phase_transitions/allen-cahn/allen-cahn.cpp b/examples/phase_transitions/allen-cahn/allen-cahn.cpp index eb6aea0..ffff406 100644 --- a/examples/phase_transitions/allen-cahn/allen-cahn.cpp +++ b/examples/phase_transitions/allen-cahn/allen-cahn.cpp @@ -13,36 +13,36 @@ namespace MMSP{ void generate(int dim, const char* filename) { if (dim==1) { - MMSP::grid<1,double> grid(1,0,128); + GRID1D initGrid(1,0,128); - for (int i=0; i grid(1,0,128,0,128); + GRID2D initGrid(1,0,128,0,128); - for (int i=0; i grid(1,0,64,0,64,0,64); + GRID3D initGrid(1,0,64,0,64,0,64); - for (int i=0; i void update(MMSP::grid& grid, int steps) +template void update(grid& oldGrid, int steps) { - MMSP::grid update(grid); + grid newGrid(oldGrid); double r = 1.0; double u = 1.0; @@ -51,12 +51,12 @@ template void update(MMSP::grid& grid, int steps) double dt = 0.01; for (int step=0; step > grid(2,0,edge,0,edge); // field 0 is c, field 1 is mu + grid<2,vector > initGrid(2,0,edge,0,edge); // field 0 is c, field 1 is mu for (int d=0; d x = position(grid,n); - double wave = x[0]*dx(grid,0)*q[0] + x[1]*dx(grid,1)*q[1]; - grid(n)[0] = C0*(1.0 + 0.1 * std::cos(wave)); + for (int n=0; n x = position(initGrid,n); + double wave = x[0]*dx(initGrid,0)*q[0] + x[1]*dx(initGrid,1)*q[1]; + initGrid(n)[0] = C0*(1.0 + 0.1 * std::cos(wave)); } #endif - ghostswap(grid); // otherwise, parallel jobs have a "window frame" artifact + ghostswap(initGrid); // otherwise, parallel jobs have a "window frame" artifact - for (int n=0; n x = position(grid,n); - grid(n)[1] = full_dfdc(grid(n)[0]) - K*field_laplacian(grid, x, 0); + for (int n=0; n x = position(initGrid,n); + initGrid(n)[1] = full_dfdc(initGrid(n)[0]) - K*field_laplacian(initGrid, x, 0); } double dV = 1.0; for (int d=0; d x = position(grid,n); - MMSP::vector > gradC = grad(grid, x); + for (int n=0; n x = position(initGrid,n); + vector > gradC = grad(initGrid, x); double magSqGradC = 0.0; for (int d=0; d -void update(MMSP::grid >& oldGrid, int steps) +void update(grid >& oldGrid, int steps) { int rank=0; #ifdef MPI_VERSION @@ -273,7 +273,7 @@ void update(MMSP::grid >& oldGrid, int steps) ghostswap(oldGrid); - MMSP::grid > newGrid(oldGrid); // new values at each point and initial guess for iteration + grid > newGrid(oldGrid); // new values at each point and initial guess for iteration newGrid.copy(oldGrid); // deep copy: includes data and ghost cells. Expensive. @@ -325,7 +325,7 @@ void update(MMSP::grid >& oldGrid, int steps) #pragma omp parallel for schedule(dynamic) #endif for (int n=0; n x = position(oldGrid,n); + vector x = position(oldGrid,n); int x_sum=0; for (int d=0; d >& oldGrid, int steps) #pragma omp parallel for schedule(dynamic) #endif for (int n=0; n x = position(oldGrid,n); + vector x = position(oldGrid,n); T lapC = field_laplacian(newGrid, x, 0); T lapU = field_laplacian(newGrid, x, 1); @@ -439,8 +439,8 @@ void update(MMSP::grid >& oldGrid, int steps) double energy = 0.0; double mass = 0.0; for (int n=0; n x = position(newGrid,n); - MMSP::vector > gradC = grad(newGrid, x); + vector x = position(newGrid,n); + vector > gradC = grad(newGrid, x); double gradCsq = 0.0; for (int d=0; d grid(1,0,128); + GRID1D initGrid(1,0,128); - for (int i=0; i grid(1,0,128,0,128); + GRID2D initGrid(1,0,128,0,128); - for (int i=0; i grid(1,0,64,0,64,0,64); + GRID3D initGrid(1,0,64,0,64,0,64); - for (int i=0; i void update(MMSP::grid& grid, int steps) +template void update(grid& oldGrid, int steps) { - MMSP::grid update(grid); - MMSP::grid temp(grid); + grid newGrid(oldGrid); + grid temp(oldGrid); double r = 1.0; double u = 1.0; @@ -52,17 +52,17 @@ template void update(MMSP::grid& grid, int steps) double dt = 0.01; for (int step=0; step grid(1,0,128); + GRID1D initGrid(1,0,128); - for (int i=0; i grid(1,0,128,0,128); + GRID2D initGrid(1,0,128,0,128); - for (int i=0; i grid(1,0,64,0,64,0,64); + GRID3D initGrid(1,0,64,0,64,0,64); - for (int i=0; i void update(MMSP::grid& grid, int steps) +template void update(grid& oldGrid, int steps) { - MMSP::grid update(grid); + grid newGrid(oldGrid); - double r = 1.0; - double u = 1.0; - double K = 1.0; - double M = 1.0; - double dt = 0.01; - double kT = 0.01; - double dV = 1.0; + T r = 1.0; + T u = 1.0; + T K = 1.0; + T M = 1.0; + T dt = 0.01; + T kT = 0.01; + T dV = 1.0; for (int step=0; step grid(1,0,128); + GRID1D initGrid(1,0,128); - for (int i=0; i grid(1,0,128,0,128); + GRID2D initGrid(1,0,128,0,128); - for (int i=0; i grid(1,0,64,0,64,0,64); + GRID3D initGrid(1,0,64,0,64,0,64); - for (int i=0; i void update(MMSP::grid& grid, int steps) +template void update(grid& oldGrid, int steps) { - MMSP::grid update(grid); - MMSP::grid temp(grid); + grid newGrid(oldGrid); + grid temp(oldGrid); - double r = 1.0; - double u = 1.0; - double K = 1.0; - double M = 1.0; - double dt = 0.01; - double kT = 0.01; - double dV = 1.0; + T r = 1.0; + T u = 1.0; + T K = 1.0; + T M = 1.0; + T dt = 0.01; + T kT = 0.01; + T dV = 1.0; for (int step=0; step > grid(2,0,edge,0,edge); - for (int d=0; d x = position(grid,i); + initGrid(i)[1]=undercooling; // Initial undercooling + vector x = position(initGrid,i); int r=sqrt(pow(x[0]-edge/2,2)+pow(x[1]-edge/2,2)); - if (r<=R) grid(i)[0]=1.; - else grid(i)[0]=0.; + if (r<=R) initGrid(i)[0]=1.; + else initGrid(i)[0]=0.; } - output(grid,filename); + output(initGrid,filename); } else { @@ -43,7 +41,7 @@ void generate(int dim, const char* filename) } } -template void update(MMSP::grid >& grid, int steps) +template void update(grid >& refGrid, int steps) { int id=0; int np=1; @@ -53,11 +51,11 @@ template void update(MMSP::grid >& grid, int steps) #endif static int iterations=1; - static MMSP::grid<2,double> grid_old(1,g0(grid,0),g1(grid,0),g0(grid,1),g1(grid,1)); + static grid<2,T> oldGrid(1,g0(refGrid,0),g1(refGrid,0),g0(refGrid,1),g1(refGrid,1)); if (iterations==1) - for (int i=0; i > update(grid); + grid > newGrid(refGrid); double dt=5e-5; // time-step double theta=0.; // angle relative to lab frame double c=0.02; // degree of anisotropy @@ -67,7 +65,7 @@ template void update(MMSP::grid >& grid, int steps) double k1=0.9; double k2=20.; double DiffT=2.25; // thermal diffusivity - double CFL=tau/(2*alpha*alpha*(1./pow(dx(grid,0),2)+1./pow(dx(grid,1),2))); // Courant-Friedrich-Lewy condition on dt + double CFL=tau/(2*alpha*alpha*(1./pow(dx(refGrid,0),2)+1./pow(dx(refGrid,1),2))); // Courant-Friedrich-Lewy condition on dt if (dt>0.5*CFL) { @@ -83,27 +81,27 @@ template void update(MMSP::grid >& grid, int steps) for (int step=0; step > Dgradphi(grid); + ghostswap(refGrid); + grid > Dgradphi(refGrid); - for (int i=0; i x=position(grid,i); + vector x=position(refGrid,i); // calculate grad(phi) - vector gradphi(dim,0.); // (0,0) + vector gradphi(dim,0.); // (0,0) for (int d=0; d void update(MMSP::grid >& grid, int steps) // Sync parallel grids ghostswap(Dgradphi); - for (int i=0; i x = position(grid,i); + vector x = position(refGrid,i); // Update phase field - double divDgradphi = 0.; + T divDgradphi = 0.; for (int d=0; d old=grid(i); - double m_phi=old[0]-0.5-(k1/M_PI)*atan(k2*old[1]); + vector old=refGrid(i); + T m_phi=old[0]-0.5-(k1/M_PI)*atan(k2*old[1]); // Semi-implicit scheme per Warren 2003 if (m_phi>0) { - update(x)[0] = ((m_phi+tau/dt)*old[0]+divDgradphi)/(tau/dt+old[0]*m_phi); + newGrid(x)[0] = ((m_phi+tau/dt)*old[0]+divDgradphi)/(tau/dt+old[0]*m_phi); } else { - update(x)[0] = (old[0]*tau/dt+divDgradphi)/(tau/dt-(1.-old[0])*m_phi); + newGrid(x)[0] = (old[0]*tau/dt+divDgradphi)/(tau/dt-(1.-old[0])*m_phi); } // Fully explicit forward-Euler discretization - //update(x)[0] = grid(i)[0] + dt*dphidt/tau; + //newGrid(x)[0] = refGrid(i)[0] + dt*dphidt/tau; // Update undercooling field - double lapT=0; + T lapT=0; for (int d=0; d grid(1,0,128); + GRID1D initGrid(1,0,128); - for (int i=0; i grid(1,0,128,0,128); + GRID2D initGrid(1,0,128,0,128); - for (int i=0; i grid(1,0,64,0,64,0,64); + GRID3D initGrid(1,0,64,0,64,0,64); - for (int i=0; i void update(MMSP::grid& grid, int steps) +template void update(grid& oldGrid, int steps) { - MMSP::grid update(grid); - MMSP::grid temp(grid); + grid newGrid(oldGrid); + grid temp(oldGrid); - double dt = 0.01; - double dV = 1.0; - double epsilon = 0.05; + T dt = 0.01; + T dV = 1.0; + T epsilon = 0.05; for (int step=0; step -#include"heisenberg.hpp" +#include"spinenberg.hpp" namespace MMSP{ void generate(int dim, const char* filename) { if (dim==1) { - MMSP::grid<1,vector > grid(3,0,128); + GRID1D initGrid(3,0,128); - for (int i=0; i > grid(3,0,128,0,128); + GRID2D initGrid(3,0,128,0,128); - for (int i=0; i > grid(3,0,64,0,64,0,64); + GRID3D initGrid(3,0,64,0,64,0,64); - for (int i=0; i >& grid, int steps) +template void update(grid >& spinGrid, int steps) { double J = 1.0; - double kT = 0.50; + double kT = (dim==3)?0.75:0.50; double pi = acos(-1.0); - int gss = int(sqrt(nodes(grid))); + int gss = (dim==1)?nodes(spinGrid):int(sqrt(nodes(spinGrid))); for (int step=0; step x = position(grid,p); - vector& s1 = grid(p); + int p = rand()%nodes(spinGrid); + vector x = position(spinGrid,p); + vector& s1 = spinGrid(p); - // choose a random unit vector - vector s2(3); + // choose a random unit vector + vector s2(3); double psi = 2.0*pi*double(rand())/double(RAND_MAX); double theta = acos(1.0-2.0*double(rand())/double(RAND_MAX)); s2[0] = cos(psi)*sin(theta); @@ -80,62 +80,33 @@ void update(grid<2,vector >& grid, int steps) // compute energy change double sum = -1.0; - for (int i=-1; i<=1; i++) - for (int j=-1; j<=1; j++) { - vector& s = grid[x[0]+i][x[1]+j]; + if (dim==1) { + for (int i=-1; i<=1; i++) { + vector& s = spinGrid[x[0]+i]; sum += s[0]*(s1[0]-s2[0])+s[1]*(s1[1]-s2[1])+s[2]*(s1[2]-s2[2]); } - double dE = -J*sum; - - // attempt a spin flip - double r = double(rand())/double(RAND_MAX); - if (dE<=0.0) grid(p) = s2; - else if (r >& grid, int steps) -{ - double J = 1.0; - double kT = 0.75; - double pi = acos(-1.0); - - int gss = int(sqrt(nodes(grid))); - - for (int step=0; step x = position(grid,p); - vector& s1 = grid(p); - - // choose a random unit vector - vector s2(3); - double psi = 2.0*pi*double(rand())/double(RAND_MAX); - double theta = acos(1.0-2.0*double(rand())/double(RAND_MAX)); - s2[0] = cos(psi)*sin(theta); - s2[1] = sin(psi)*sin(theta); - s2[2] = cos(theta); - - // compute energy change - double sum = -1.0; - for (int i=-1; i<=1; i++) - for (int j=-1; j<=1; j++) - for (int k=-1; k<=1; k++) { - vector& s = grid[x[0]+i][x[1]+j][x[2]+k]; + } else if (dim==2) { + for (int i=-1; i<=1; i++) + for (int j=-1; j<=1; j++) { + vector& s = spinGrid[x[0]+i][x[1]+j]; sum += s[0]*(s1[0]-s2[0])+s[1]*(s1[1]-s2[1])+s[2]*(s1[2]-s2[2]); } + } else if (dim==3) { + for (int i=-1; i<=1; i++) + for (int j=-1; j<=1; j++) + for (int k=-1; k<=1; k++) { + vector& s = spinGrid[x[0]+i][x[1]+j][x[2]+k]; + sum += s[0]*(s1[0]-s2[0])+s[1]*(s1[1]-s2[1])+s[2]*(s1[2]-s2[2]); + } + } double dE = -J*sum; // attempt a spin flip double r = double(rand())/double(RAND_MAX); - if (dE<=0.0) grid(p) = s2; - else if (r grid(1,0,128); + GRID1D initGrid(1,0,128); - for (int i=0; i grid(1,0,128,0,128); + GRID2D initGrid(1,0,128,0,128); - for (int i=0; i grid(1,0,64,0,64,0,64); + GRID3D initGrid(1,0,64,0,64,0,64); - for (int i=0; i& grid, int steps) +template void update(grid& spinGrid, int steps) { double J = 2.0; double H = 1.0; - double kT = 0.50; + double kT = (dim==2)?0.50:0.75; - int gss = int(sqrt(nodes(grid))); + int gss = (dim==1)?nodes(spinGrid):int(sqrt(nodes(spinGrid))); for (int step=0; step x = position(grid,p); - int spin1 = grid(p); + int p = rand()%nodes(spinGrid); + vector x = position(spinGrid,p); + int spin1 = spinGrid(p); // new spin is opposite old spin int spin2 = -spin1; // compute energy change double sum = -1.0; - for (int i=-1; i<=1; i++) - for (int j=-1; j<=1; j++) { - int spin = grid[x[0]+i][x[1]+j]; + if (dim==1) { + for (int i=-1; i<=1; i++) { + int spin = spinGrid[x[0]+i]; sum += (spin!=spin2)-(spin!=spin1); } - double dE = -0.5*J*sum-H*(spin2-spin1); - - // attempt a spin flip - double r = double(rand())/double(RAND_MAX); - if (dE<=0.0) grid(p) = spin2; - else if (r& grid, int steps) -{ - double J = 2.0; - double H = 1.0; - double kT = 0.75; - - int gss = int(sqrt(nodes(grid))); - - for (int step=0; step x = position(grid,p); - int spin1 = grid(p); - - // new spin is opposite old spin - int spin2 = -spin1; - - // compute energy change - double sum = -1.0; - for (int i=-1; i<=1; i++) - for (int j=-1; j<=1; j++) - for (int k=-1; k<=1; k++) { - int spin = grid[x[0]+i][x[1]+j][x[2]+k]; + } else if (dim==2) { + for (int i=-1; i<=1; i++) + for (int j=-1; j<=1; j++) { + int spin = spinGrid[x[0]+i][x[1]+j]; sum += (spin!=spin2)-(spin!=spin1); } + } else if (dim==3) { + for (int i=-1; i<=1; i++) + for (int j=-1; j<=1; j++) + for (int k=-1; k<=1; k++) { + int spin = spinGrid[x[0]+i][x[1]+j][x[2]+k]; + sum += (spin!=spin2)-(spin!=spin1); + } + } double dE = -0.5*J*sum-H*(spin2-spin1); // attempt a spin flip double r = double(rand())/double(RAND_MAX); - if (dE<=0.0) grid(p) = spin2; - else if (r grid(1,0,128); + GRID1D initGrid(1,0,128); - for (int i=0; i grid(1,0,128,0,128); + GRID2D initGrid(1,0,128,0,128); - for (int i=0; i grid(1,0,64,0,64,0,64); + GRID3D initGrid(1,0,64,0,64,0,64); - for (int i=0; i& grid, int steps) +template void update(grid& spinGrid, int steps) { int Q = 20; double J = 1.0; - double kT = 0.50; + double kT = (dim==3)?0.75:0.50; - int gss = int(sqrt(nodes(grid))); + int gss = (dim==1)?nodes(spinGrid):int(sqrt(nodes(spinGrid))); for (int step=0; step x = position(grid,p); - int spin1 = grid(p); + int p = rand()%nodes(spinGrid); + vector x = position(spinGrid,p); + int spin1 = spinGrid(p); // choose a new spin randomly int spin2 = rand()%Q; // compute energy change double dE = -1.0; - for (int i=-1; i<=1; i++) - for (int j=-1; j<=1; j++) { - int spin = grid[x[0]+i][x[1]+j]; + if (dim==2) { + for (int i=-1; i<=1; i++) { + int spin = spinGrid[x[0]+i]; dE += (spin!=spin2)-(spin!=spin1); } - dE *= J; - - // attempt a spin flip - double r = double(rand())/double(RAND_MAX); - if (dE<=0.0) grid(p) = spin2; - else if (r& grid, int steps) -{ - int Q = 20; - double J = 1.0; - double kT = 0.75; - - int gss = int(sqrt(nodes(grid))); - - for (int step=0; step x = position(grid,p); - int spin1 = grid(p); - - // choose a new spin randomly - int spin2 = rand()%Q; - - // compute energy change - double dE = -1.0; - for (int i=-1; i<=1; i++) - for (int j=-1; j<=1; j++) - for (int k=-1; k<=1; k++) { - int spin = grid[x[0]+i][x[1]+j][x[2]+k]; + } else if (dim==2) { + for (int i=-1; i<=1; i++) + for (int j=-1; j<=1; j++) { + int spin = spinGrid[x[0]+i][x[1]+j]; dE += (spin!=spin2)-(spin!=spin1); } + } else if (dim==3) { + for (int i=-1; i<=1; i++) + for (int j=-1; j<=1; j++) + for (int k=-1; k<=1; k++) { + int spin = grid[x[0]+i][x[1]+j][x[2]+k]; + dE += (spin!=spin2)-(spin!=spin1); + } + } dE *= J; // attempt a spin flip double r = double(rand())/double(RAND_MAX); - if (dE<=0.0) grid(p) = spin2; - else if (r Date: Tue, 2 Feb 2016 12:05:23 -0500 Subject: [PATCH 07/83] field laplacian return type --- include/MMSP.grid.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/MMSP.grid.hpp b/include/MMSP.grid.hpp index da17914..3107395 100644 --- a/include/MMSP.grid.hpp +++ b/include/MMSP.grid.hpp @@ -2490,7 +2490,7 @@ template vector laplacian(const grid >& return laplacian(GRID, x); } -template vector laplacian(const grid >& GRID, int i, int f) +template T laplacian(const grid >& GRID, int i, int f) { vector x = GRID.position(i); return laplacian(GRID, x, f); From dea62160f17a894f47dc8b1d9ed6f39bbda716c0 Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Tue, 2 Feb 2016 13:14:22 -0500 Subject: [PATCH 08/83] repair example build errors --- algorithms/evolvers.cpp | 24 --- examples/beginners_diffusion/Makefile | 4 +- .../isotropic/phase_field/ostwald.cpp | 49 ++---- .../anisotropic/Monte_Carlo/zener.cpp | 74 ++++++--- .../elliptic/Poisson/Makefile | 4 +- .../elliptic/Poisson/multigrid.hpp | 26 ++- .../elliptic/Poisson/poisson.cpp | 154 +++++++++--------- .../cahn-hilliard/explicit/Makefile | 2 +- .../solidification/anisotropic/dendritic.cpp | 6 +- .../Heisenberg/heisenberg.cpp | 35 ++-- .../statistical_mechanics/Ising/ising.cpp | 33 +++- .../statistical_mechanics/Potts/potts.cpp | 27 ++- include/MMSP.utility.hpp | 4 +- 13 files changed, 246 insertions(+), 196 deletions(-) diff --git a/algorithms/evolvers.cpp b/algorithms/evolvers.cpp index 232c626..c9bebaf 100644 --- a/algorithms/evolvers.cpp +++ b/algorithms/evolvers.cpp @@ -14,30 +14,6 @@ #include "evolvers.h" -void print_progress(const int step, const int steps, const int iterations) -{ - char* timestring; - static unsigned long tstart; - struct tm* timeinfo; - - if (step==0) { - tstart = time(NULL); - std::time_t rawtime; - std::time( &rawtime ); - timeinfo = std::localtime( &rawtime ); - timestring = std::asctime(timeinfo); - timestring[std::strlen(timestring)-1] = '\0'; - std::cout<<"Pass "< void interface_field_evolves(const T& dt, const T& width, const T& gamma, const T& epsilon, const T& w, const T& mu, const MMSP::grid >& former, MMSP::grid >& latter, const int node) diff --git a/examples/beginners_diffusion/Makefile b/examples/beginners_diffusion/Makefile index fb7ea75..4dc7d05 100644 --- a/examples/beginners_diffusion/Makefile +++ b/examples/beginners_diffusion/Makefile @@ -5,6 +5,8 @@ CCOPTS = -g PGOPTS = LIBS = -lm +core = ../../include + # If you have gnuplot pipes on your system, change this # value to 'yes' and change the options in the source # code to get graphical output. @@ -39,7 +41,7 @@ go : $(OBJECTS) # Object defs below here. 1stDiffusion.o : 1stDiffusion.cpp - $(CC) $(CCOPTIONS) -c 1stDiffusion.cpp + $(CC) $(CCOPTIONS) -I$(core) -c 1stDiffusion.cpp gnuplot_i.hpp.gch : gnuplot_i.hpp $(CC) $(CCOPTIONS) -c gnuplot_i.hpp diff --git a/examples/coarsening/ostwald_ripening/isotropic/phase_field/ostwald.cpp b/examples/coarsening/ostwald_ripening/isotropic/phase_field/ostwald.cpp index 0ceeffe..3fcb7be 100644 --- a/examples/coarsening/ostwald_ripening/isotropic/phase_field/ostwald.cpp +++ b/examples/coarsening/ostwald_ripening/isotropic/phase_field/ostwald.cpp @@ -15,14 +15,11 @@ void generate(int dim, const char* filename) if (dim==1) { GRID1D initGrid(2,0,128); - int x0 = x0(initGrid); - int x1 = x1(initGrid); - - for (int x=x0; x void update(grid >& oldGrid, int st for (int step=0; step x = position(oldGrid, n); double sum = 0.0; for (int i=1; i void update(grid& mcGrid, int steps) // determine neighboring spins sparse neighbors; if (dim==1) { - for (int i=-1; i<=1; i++) { - int spin = mcGrid[x[0]+i]; - set(neighbors,spin) = true; - } + for (int i=-1; i<2; i++) { + x[0] += i; + int spin = mcGrid(x); + set(neighbors,spin) = true; + x[0] -= i; + } } else if (dim==2) { - for (int i=-1; i<=1; i++) - for (int j=-1; j<=1; j++) { - int spin = mcGrid[x[0]+i][x[1]+j]; + for (int i=-1; i<2; i++) { + x[0] += i; + for (int j=-1; j<2; j++) { + x[1] += j; + int spin = mcGrid(x); set(neighbors,spin) = true; + x[1] -= j; } + x[0] -= i; + } } else if (dim==3) { - for (int i=-1; i<=1; i++) - for (int j=-1; j<=1; j++) - for (int k=-1; k<=1; k++) { - int spin = mcGrid[x[0]+i][x[1]+j][x[2]+k]; + for (int i=-1; i<2; i++) { + x[0] += i; + for (int j=-1; j<2; j++) { + x[1] += j; + for (int k=-1; k<2; k++) { + x[2] += k; + int spin = mcGrid(x); set(neighbors,spin) = true; + x[2] -= k; } + x[1] -= j; + } + x[0] -= i; + } } // choose a random neighbor spin @@ -119,23 +134,38 @@ template void update(grid& mcGrid, int steps) // compute energy change double dE = -energy(spin1,spin2); if (dim==1) { - for (int i=-1; i<=1; i++) { - int spin = mcGrid[x[0]+i]; - dE += energy(spin,spin2)-energy(spin,spin1); - } + for (int i=-1; i<2; i++) { + x[0] += i; + int spin = mcGrid(x); + dE += energy(spin,spin2)-energy(spin,spin1); + x[0] -= i; + } } else if (dim==2) { - for (int i=-1; i<=1; i++) - for (int j=-1; j<=1; j++){ - int spin = mcGrid[x[0]+i][x[1]+j]; + for (int i=-1; i<2; i++) { + x[0] += i; + for (int j=-1; j<2; j++){ + x[1] += j; + int spin = mcGrid(x); dE += energy(spin,spin2)-energy(spin,spin1); + x[1] -= j; } + x[0] -= i; + } } else if (dim==3) { - for (int i=-1; i<=1; i++) - for (int j=-1; j<=1; j++) - for (int k=-1; k<=1; k++) { - int spin = mcGrid[x[0]+i][x[1]+j][x[2]+k]; + for (int i=-1; i<2; i++) { + x[0] += i; + for (int j=-1; j<2; j++) { + x[1] += j; + for (int k=-1; k<2; k++) { + x[2] += k; + int spin = mcGrid(x); dE += energy(spin,spin2)-energy(spin,spin1); + x[2] -= k; } + x[1] -= j; + } + x[0] -= i; + } } // compute boundary energy, mobility diff --git a/examples/differential_equations/elliptic/Poisson/Makefile b/examples/differential_equations/elliptic/Poisson/Makefile index 048e6d0..85f40ac 100644 --- a/examples/differential_equations/elliptic/Poisson/Makefile +++ b/examples/differential_equations/elliptic/Poisson/Makefile @@ -16,10 +16,10 @@ core = $(incdir)/MMSP.hpp \ $(incdir)/MMSP.grid.hpp # the program -poisson: poisson.cpp multigrid.hpp $(core) +poisson: poisson.cpp $(core) $(compiler) $(flags) $< -o $@ -lz -parallel: poisson.cpp multigrid.hpp $(core) +parallel: poisson.cpp $(core) $(pcompiler) $(pflags) $< -o $@ -lz clean: diff --git a/examples/differential_equations/elliptic/Poisson/multigrid.hpp b/examples/differential_equations/elliptic/Poisson/multigrid.hpp index e8d4bf1..36851e4 100644 --- a/examples/differential_equations/elliptic/Poisson/multigrid.hpp +++ b/examples/differential_equations/elliptic/Poisson/multigrid.hpp @@ -9,7 +9,12 @@ namespace MMSP { -template void coarsen(MMSP::grid<2,T>& u, int stride, std::string method="full-weighting") +template void coarsen(grid<1,T>& u, int stride, std::string method="full-weighting") +{ + return; +} + +template void coarsen(grid<2,T>& u, int stride, std::string method="full-weighting") { // coarsen from stride s to stride 2s int s = stride; @@ -26,7 +31,7 @@ template void coarsen(MMSP::grid<2,T>& u, int stride, std::string m +0.0625*(u[x+s][y+s]+u[x-s][y+s]+u[x+s][y-s]+u[x-s][y-s]); } -template void coarsen(MMSP::grid<3,T>& u, int stride, std::string method="full-weighting") +template void coarsen(grid<3,T>& u, int stride, std::string method="full-weighting") { // coarsen from stride s to stride 2s int s = stride; @@ -48,7 +53,12 @@ template void coarsen(MMSP::grid<3,T>& u, int stride, std::string m +0.015625*(u[x+s][y-s][z-s]+u[x-s][y+s][z-s]+u[x-s][y-s][z+s]+u[x-s][y-s][z-s]); } -template void refine(MMSP::grid<2,T>& u, int stride, std::string method="linear") +template void refine(grid<1,T>& u, int stride, std::string method="linear") +{ + return; +} + +template void refine(grid<2,T>& u, int stride, std::string method="linear") { // refine from stride 2s to stride s int s = stride; @@ -85,7 +95,7 @@ template void refine(MMSP::grid<2,T>& u, int stride, std::string me } } } -template void refine(MMSP::grid<3,T>& u, int stride, std::string method="linear") +template void refine(grid<3,T>& u, int stride, std::string method="linear") { // refine from stride 2s to stride s int s = stride; @@ -143,7 +153,7 @@ template void refine(MMSP::grid<3,T>& u, int stride, std::string me } template -void MG(MMSP::grid& u, const MMSP::grid& f, int stride, int gamma=1, int nu1=2, int nu2=2) +void MG(grid& u, const grid& f, int stride, int gamma=1, int nu1=2, int nu2=2) { // standard multigrid cycle int s = stride; @@ -160,14 +170,14 @@ void MG(MMSP::grid& u, const MMSP::grid& f, int stride, int gamma= smooth(u,f,s,nu1); // compute defect - MMSP::grid d(u); + grid d(u); defect(u,f,d,s); // restrict defect coarsen(d,s); // solve for correction - MMSP::grid v(u); + grid v(u); v = static_cast(0.0); // multigrid iteration @@ -185,7 +195,7 @@ void MG(MMSP::grid& u, const MMSP::grid& f, int stride, int gamma= } template -void FMG(MMSP::grid& u, const MMSP::grid& f, int gamma=1, int nu1=2, int nu2=2) +void FMG(grid& u, const grid& f, int gamma=1, int nu1=2, int nu2=2) { // solve at coarsest level int s = (x1(u,0)-1)/4; diff --git a/examples/differential_equations/elliptic/Poisson/poisson.cpp b/examples/differential_equations/elliptic/Poisson/poisson.cpp index e13780d..35ed74c 100644 --- a/examples/differential_equations/elliptic/Poisson/poisson.cpp +++ b/examples/differential_equations/elliptic/Poisson/poisson.cpp @@ -11,7 +11,29 @@ namespace MMSP { template -void smooth(MMSP::grid<2,T>& u, const MMSP::grid<2,T>& f, int stride, int iterations=1) +void smooth(grid<1,T>& u, const grid<1,T>& f, int stride, int iterations=1) +{ + // red-black Gauss-Seidel iteration + int s = stride; + double dx = s*MMSP::dx(u); + double x1 = MMSP::x1(u); + double dx2 = dx*dx; + double w = 1.0/(2.0/dx2); + double wx = w/dx2; + + for (int i=0; i +void smooth(grid<2,T>& u, const grid<2,T>& f, int stride, int iterations=1) { // red-black Gauss-Seidel iteration int s = stride; @@ -40,7 +62,7 @@ void smooth(MMSP::grid<2,T>& u, const MMSP::grid<2,T>& f, int stride, int iterat } template -void smooth(MMSP::grid<3,T>& u, const MMSP::grid<3,T>& f, int stride, int iterations=1) +void smooth(grid<3,T>& u, const grid<3,T>& f, int stride, int iterations=1) { // red-black Gauss-Seidel iteration int s = stride; @@ -77,7 +99,26 @@ void smooth(MMSP::grid<3,T>& u, const MMSP::grid<3,T>& f, int stride, int iterat } template -void defect(const MMSP::grid<2,T>& u, const MMSP::grid<2,T>& f, MMSP::grid<2,T>& d, int stride) +void defect(const grid<1,T>& u, const grid<1,T>& f, grid<1,T>& d, int stride) +{ + // compute defect for Poisson equation lap(u) = f + int s = stride; + double dx = s*MMSP::dx(u); + double x1 = MMSP::x1(u); + double dx2 = dx*dx; + double wx = 1.0/dx2; + + for (int x=s; x +void defect(const grid<2,T>& u, const grid<2,T>& f, grid<2,T>& d, int stride) { // compute defect for Poisson equation lap(u) = f int s = stride; @@ -106,7 +147,7 @@ void defect(const MMSP::grid<2,T>& u, const MMSP::grid<2,T>& f, MMSP::grid<2,T>& } template -void defect(const MMSP::grid<3,T>& u, const MMSP::grid<3,T>& f, MMSP::grid<3,T>& d, int stride) +void defect(const grid<3,T>& u, const grid<3,T>& f, grid<3,T>& d, int stride) { // compute defect for Poisson equation lap(u) = f int s = stride; @@ -150,96 +191,57 @@ void defect(const MMSP::grid<3,T>& u, const MMSP::grid<3,T>& f, MMSP::grid<3,T>& void generate(int dim, const char* filename) { if (dim==1) { - MMSP::grid<1,double> grid(1,0,128); - int x0 = MMSP::x0(grid); - int x1 = MMSP::x1(grid); + grid<1,double> initGrid(1,0,128); - for (int x=x0; x x = position(initGrid, n); + double X = double(x[0])/128.0; + initGrid(n) = exp(X); } - MMSP::output(grid,filename); + output(initGrid,filename); } if (dim==2) { - MMSP::grid<2,double> grid(1,0,128,0,128); - int x0 = MMSP::x0(grid); - int x1 = MMSP::x1(grid); - int y0 = MMSP::y0(grid); - int y1 = MMSP::y1(grid); - - for (int x=x0; x grid(1,0,64,0,64,0,64); - int x0 = MMSP::x0(grid); - int x1 = MMSP::x1(grid); - int y0 = MMSP::y0(grid); - int y1 = MMSP::y1(grid); - int z0 = MMSP::z0(grid); - int z1 = MMSP::z1(grid); - - for (int x=x0; x initGrid(1,0,128,0,128); -void update(MMSP::grid<2,double>& grid, int steps) -{ - const MMSP::grid<2,double>& f = grid; - - MMSP::grid<2,double> u(f); - int x0 = MMSP::x0(u); - int x1 = MMSP::x1(u); - int y0 = MMSP::y0(u); - int y1 = MMSP::y1(u); + for (int n=0; n x = position(initGrid, n); + double X = double(x[0])/128.0; + double Y = double(x[1])/128.0; + initGrid(n) = exp(X*Y); + } - for (int x=x0; x initGrid(1,0,64,0,64,0,64); + + for (int n=0; n x = position(initGrid, n); + double X = double(x[0])/128.0; + double Y = double(x[1])/128.0; + double Z = double(x[2])/128.0; + initGrid(n) = exp(X*Y*Z); + } - MMSP::swap(grid,u); + output(initGrid,filename); + } } -void update(MMSP::grid<3,double>& grid, int steps) +template void update(grid& multiGrid, int steps) { - const MMSP::grid<3,double>& f = grid; + const grid& f = multiGrid; - MMSP::grid<3,double> u(f); - int x0 = MMSP::x0(u); - int x1 = MMSP::x1(u); - int y0 = MMSP::y0(u); - int y1 = MMSP::y1(u); - int z0 = MMSP::z0(u); - int z1 = MMSP::z1(u); + grid u(f); - for (int x=x0; x void update(grid >& refGrid, int st static int iterations=1; static grid<2,T> oldGrid(1,g0(refGrid,0),g1(refGrid,0),g0(refGrid,1),g1(refGrid,1)); if (iterations==1) - for (int i=0; i > newGrid(refGrid); double dt=5e-5; // time-step @@ -80,7 +81,8 @@ template void update(grid >& refGrid, int st int plus=0; for (int step=0; step > Dgradphi(refGrid); diff --git a/examples/statistical_mechanics/Heisenberg/heisenberg.cpp b/examples/statistical_mechanics/Heisenberg/heisenberg.cpp index da0cbe8..23d4556 100644 --- a/examples/statistical_mechanics/Heisenberg/heisenberg.cpp +++ b/examples/statistical_mechanics/Heisenberg/heisenberg.cpp @@ -6,7 +6,7 @@ #define spinENBERG_UPDATE #include"MMSP.hpp" #include -#include"spinenberg.hpp" +#include"heisenberg.hpp" namespace MMSP{ @@ -81,23 +81,38 @@ template void update(grid >& spinGrid, int s // compute energy change double sum = -1.0; if (dim==1) { - for (int i=-1; i<=1; i++) { - vector& s = spinGrid[x[0]+i]; + for (int i=-1; i<2; i++) { + x[0] += i; + vector& s = spinGrid(x); sum += s[0]*(s1[0]-s2[0])+s[1]*(s1[1]-s2[1])+s[2]*(s1[2]-s2[2]); + x[0] -= i; } } else if (dim==2) { - for (int i=-1; i<=1; i++) - for (int j=-1; j<=1; j++) { - vector& s = spinGrid[x[0]+i][x[1]+j]; + for (int i=-1; i<2; i++) { + x[0] += i; + for (int j=-1; j<2; j++) { + x[1] += j; + vector& s = spinGrid(x); sum += s[0]*(s1[0]-s2[0])+s[1]*(s1[1]-s2[1])+s[2]*(s1[2]-s2[2]); + x[1] -= j; } + x[0] -= i; + } } else if (dim==3) { - for (int i=-1; i<=1; i++) - for (int j=-1; j<=1; j++) - for (int k=-1; k<=1; k++) { - vector& s = spinGrid[x[0]+i][x[1]+j][x[2]+k]; + for (int i=-1; i<2; i++) { + x[0] += i; + for (int j=-1; j<2; j++) { + x[1] += j; + for (int k=-1; k<2; k++) { + x[2] += k; + vector& s = spinGrid(x); sum += s[0]*(s1[0]-s2[0])+s[1]*(s1[1]-s2[1])+s[2]*(s1[2]-s2[2]); + x[2] -= k; } + x[1] -= j; + } + x[0] -= i; + } } double dE = -J*sum; diff --git a/examples/statistical_mechanics/Ising/ising.cpp b/examples/statistical_mechanics/Ising/ising.cpp index 979d276..113075c 100644 --- a/examples/statistical_mechanics/Ising/ising.cpp +++ b/examples/statistical_mechanics/Ising/ising.cpp @@ -61,23 +61,38 @@ template void update(grid& spinGrid, int steps) // compute energy change double sum = -1.0; if (dim==1) { - for (int i=-1; i<=1; i++) { - int spin = spinGrid[x[0]+i]; + for (int i=-1; i<2; i++) { + x[0] += i; + int spin = spinGrid(x); sum += (spin!=spin2)-(spin!=spin1); + x[0] -= i; } } else if (dim==2) { - for (int i=-1; i<=1; i++) - for (int j=-1; j<=1; j++) { - int spin = spinGrid[x[0]+i][x[1]+j]; + for (int i=-1; i<2; i++) { + x[0] += i; + for (int j=-1; j<2; j++) { + x[1] += j; + int spin = spinGrid(x); sum += (spin!=spin2)-(spin!=spin1); + x[1] -= j; } + x[0] -= i; + } } else if (dim==3) { - for (int i=-1; i<=1; i++) - for (int j=-1; j<=1; j++) - for (int k=-1; k<=1; k++) { - int spin = spinGrid[x[0]+i][x[1]+j][x[2]+k]; + for (int i=-1; i<2; i++) { + x[0] += i; + for (int j=-1; j<2; j++) { + x[1] += j; + for (int k=-1; k<2; k++) { + x[2] += k; + int spin = spinGrid(x); sum += (spin!=spin2)-(spin!=spin1); + x[2] -= k; } + x[1] -= j; + } + x[0] -= i; + } } double dE = -0.5*J*sum-H*(spin2-spin1); diff --git a/examples/statistical_mechanics/Potts/potts.cpp b/examples/statistical_mechanics/Potts/potts.cpp index 1bbd914..ca33508 100644 --- a/examples/statistical_mechanics/Potts/potts.cpp +++ b/examples/statistical_mechanics/Potts/potts.cpp @@ -62,22 +62,37 @@ template void update(grid& spinGrid, int steps) double dE = -1.0; if (dim==2) { for (int i=-1; i<=1; i++) { - int spin = spinGrid[x[0]+i]; + x[0] += i; + int spin = spinGrid(x); dE += (spin!=spin2)-(spin!=spin1); + x[0] -= i; } } else if (dim==2) { - for (int i=-1; i<=1; i++) + for (int i=-1; i<=1; i++) { + x[0] += i; for (int j=-1; j<=1; j++) { - int spin = spinGrid[x[0]+i][x[1]+j]; + x[1] += j; + int spin = spinGrid(x); dE += (spin!=spin2)-(spin!=spin1); + x[1] -= j; } + x[0] -= i; + } } else if (dim==3) { - for (int i=-1; i<=1; i++) - for (int j=-1; j<=1; j++) + for (int i=-1; i<=1; i++) { + x[0] += i; + for (int j=-1; j<=1; j++) { + x[1] += j; for (int k=-1; k<=1; k++) { - int spin = grid[x[0]+i][x[1]+j][x[2]+k]; + x[2] += k; + int spin = spinGrid(x); dE += (spin!=spin2)-(spin!=spin1); + x[2] -= k; } + x[1] -= j; + } + x[0] -= i; + } } dE *= J; diff --git a/include/MMSP.utility.hpp b/include/MMSP.utility.hpp index 996e2fd..2522baa 100644 --- a/include/MMSP.utility.hpp +++ b/include/MMSP.utility.hpp @@ -329,8 +329,8 @@ template T global(T& value, const char* operation) { Call once inside the update function (or equivalent). for (int step=0; step Date: Tue, 2 Feb 2016 14:06:22 -0500 Subject: [PATCH 09/83] boundary-aware grid accessors, templating --- .../anisotropic/Monte_Carlo/graingrowth.cpp | 2 + .../anisotropic/sparsePF/graingrowth.cpp | 2 +- .../isotropic/phase_field/graingrowth.cpp | 33 ++-- .../isotropic/phase_field/ostwald.cpp | 4 +- .../anisotropic/Monte_Carlo/zener.cpp | 27 ++-- .../anisotropic/phase_field/zener.cpp | 39 ++--- .../anisotropic/sparsePF/zener.cpp | 39 ++--- .../isotropic/Monte_Carlo/zener.cpp | 151 +++++------------- .../isotropic/phase_field/zener.cpp | 39 ++--- .../isotropic/sparsePF/zener.cpp | 39 ++--- 10 files changed, 168 insertions(+), 207 deletions(-) diff --git a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp index 8dac8a7..8b745a3 100644 --- a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp +++ b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp @@ -34,6 +34,7 @@ void generate(int dim, const char* filename) for (int i=0; i void update(grid >& oldGrid, int st for (int n=0; n x = position(oldGrid,n); - // determine nonzero fields within + // determine nonzero fields within // the neighborhood of this node sparse s; for (int j=0; j x = position(initGrid,i); - initGrid(x)[0] = 0.0; - initGrid(x)[1] = 0.0; double d = 64.0-x[0]; - if (d<32.0) initGrid(x)[1] = 1.0; - else initGrid(x)[0] = 1.0; + if (d<32.0) { + initGrid(i)[0] = 0.0; + initGrid(i)[1] = 1.0; + } else { + initGrid(i)[0] = 1.0; + initGrid(i)[1] = 0.0; + } } output(initGrid,filename); @@ -32,11 +35,14 @@ void generate(int dim, const char* filename) for (int i=0; i x = position(initGrid,i); - initGrid(x)[0] = 0.0; - initGrid(x)[1] = 0.0; double d = sqrt(pow(64.0-x[0],2)+pow(64.0-x[1],2)); - if (d<32.0) initGrid(x)[1] = 1.0; - else initGrid(x)[0] = 1.0; + if (d<32.0) { + initGrid(i)[0] = 0.0; + initGrid(i)[1] = 1.0; + } else { + initGrid(i)[0] = 1.0; + initGrid(i)[1] = 0.0; + } } output(initGrid,filename); @@ -47,11 +53,14 @@ void generate(int dim, const char* filename) for (int i=0; i x = position(initGrid,i); - initGrid(x)[0] = 0.0; - initGrid(x)[1] = 0.0; double d = sqrt(pow(32.0-x[0],2)+pow(32.0-x[1],2)+pow(32.0-x[2],2)); - if (d<16.0) initGrid(x)[1] = 1.0; - else initGrid(x)[0] = 1.0; + if (d<16.0) { + initGrid(i)[0] = 0.0; + initGrid(i)[1] = 1.0; + } else { + initGrid(i)[0] = 1.0; + initGrid(i)[1] = 0.0; + } } output(initGrid,filename); diff --git a/examples/coarsening/ostwald_ripening/isotropic/phase_field/ostwald.cpp b/examples/coarsening/ostwald_ripening/isotropic/phase_field/ostwald.cpp index 3fcb7be..70b3c8a 100644 --- a/examples/coarsening/ostwald_ripening/isotropic/phase_field/ostwald.cpp +++ b/examples/coarsening/ostwald_ripening/isotropic/phase_field/ostwald.cpp @@ -92,7 +92,7 @@ template void update(grid >& oldGrid, int st T lap = laplacian(wspace,x); T C = oldGrid(n)[0]; - newGrid(x)[0] = C+dt*D*lap; + newGrid(n)[0] = C+dt*D*lap; double sum = 0.0; for (int i=1; i void update(grid >& oldGrid, int st vector vlap = laplacian(oldGrid,x); for (int i=1; i p = position(initGrid,i); - for (int x=p[0]-1; x<=p[0]+1; x++) - initGrid[x] = 0; + vector x = position(initGrid,i); + vector p(x); + for (p[0]=x[0]-1; p[0]<=x[0]+1; p[0]++) + initGrid(p) = 0; } output(initGrid,filename); @@ -45,10 +46,11 @@ void generate(int dim, const char* filename) for (int j=0; j<50; j++) { int i = rand()%nodes(initGrid); - vector p = position(initGrid,i); - for (int x=p[0]-1; x<=p[0]+1; x++) - for (int y=p[1]-1; y<=p[1]+1; y++) - initGrid[x][y] = 0; + vector x = position(initGrid,i); + vector p(x); + for (p[0]=x[0]-1; p[0]<=x[0]+1; p[0]++) + for (p[1]=x[1]-1; p[1]<=x[1]+1; p[1]++) + initGrid(p) = 0; } output(initGrid,filename); @@ -66,11 +68,12 @@ void generate(int dim, const char* filename) for (int j=0; j<50; j++) { int i = rand()%nodes(initGrid); - vector p = position(initGrid,i); - for (int x=p[0]-1; x<=p[0]+1; x++) - for (int y=p[1]-1; y<=p[1]+1; y++) - for (int z=p[2]-1; z<=p[2]+1; z++) - initGrid[x][y][z] = 0; + vector x = position(initGrid,i); + vector p(x); + for (p[0]=x[0]-1; p[0]<=x[0]+1; p[0]++) + for (p[1]=x[1]-1; p[1]<=x[1]+1; p[1]++) + for (p[2]=x[2]-1; p[2]<=x[2]+1; p[2]++) + initGrid(p) = 0; } output(initGrid,filename); diff --git a/examples/coarsening/zener_pinning/anisotropic/phase_field/zener.cpp b/examples/coarsening/zener_pinning/anisotropic/phase_field/zener.cpp index 88ad115..87857db 100644 --- a/examples/coarsening/zener_pinning/anisotropic/phase_field/zener.cpp +++ b/examples/coarsening/zener_pinning/anisotropic/phase_field/zener.cpp @@ -27,11 +27,12 @@ void generate(int dim, const char* filename) for (int j=0; j<50; j++) { int i = rand()%nodes(initGrid); - vector p = position(initGrid,i); - for (int x=p[0]-1; x<=p[0]+1; x++) { - initGrid[x][0] = 1.0; - initGrid[x][1] = 0.0; - initGrid[x][2] = 0.0; + vector x = position(initGrid,i); + vector p(x); + for (p[0]=x[0]-1; p[0]<=x[0]+1; p[0]++) { + initGrid(p)[0] = 1.0; + initGrid(p)[1] = 0.0; + initGrid(p)[2] = 0.0; } } @@ -52,12 +53,13 @@ void generate(int dim, const char* filename) for (int j=0; j<50; j++) { int i = rand()%nodes(initGrid); - vector p = position(initGrid,i); - for (int x=p[0]-1; x<=p[0]+1; x++) - for (int y=p[1]-1; y<=p[1]+1; y++) { - initGrid[x][y][0] = 1.0; - initGrid[x][y][1] = 0.0; - initGrid[x][y][2] = 0.0; + vector x = position(initGrid,i); + vector p(x); + for (p[0]=x[0]-1; p[0]<=x[0]+1; p[0]++) + for (p[1]=x[1]-1; p[1]<=x[1]+1; p[1]++) { + initGrid(p)[0] = 1.0; + initGrid(p)[1] = 0.0; + initGrid(p)[2] = 0.0; } } @@ -78,13 +80,14 @@ void generate(int dim, const char* filename) for (int j=0; j<50; j++) { int i = rand()%nodes(initGrid); - vector p = position(initGrid,i); - for (int x=p[0]-1; x<=p[0]+1; x++) - for (int y=p[1]-1; y<=p[1]+1; y++) - for (int z=p[1]-1; z<=p[2]+1; z++) { - initGrid[x][y][z][0] = 1.0; - initGrid[x][y][z][1] = 0.0; - initGrid[x][y][z][2] = 0.0; + vector x = position(initGrid,i); + vector p(x); + for (p[0]=x[0]-1; p[0]<=x[0]+1; p[0]++) + for (p[1]=x[1]-1; p[1]<=x[1]+1; p[1]++) + for (p[2]=x[2]-1; p[2]<=x[2]+1; p[2]++) { + initGrid(p)[0] = 1.0; + initGrid(p)[1] = 0.0; + initGrid(p)[2] = 0.0; } } diff --git a/examples/coarsening/zener_pinning/anisotropic/sparsePF/zener.cpp b/examples/coarsening/zener_pinning/anisotropic/sparsePF/zener.cpp index 5b931b1..32c3132 100644 --- a/examples/coarsening/zener_pinning/anisotropic/sparsePF/zener.cpp +++ b/examples/coarsening/zener_pinning/anisotropic/sparsePF/zener.cpp @@ -25,11 +25,12 @@ void generate(int dim, const char* filename) for (int j=0; j<50; j++) { int i = rand()%nodes(initGrid); - vector p = position(initGrid,i); - for (int x=p[0]-1; x<=p[0]+1; x++) { - set(initGrid[x],0) = 1.0; - set(initGrid[x],1) = 0.0; - set(initGrid[x],2) = 0.0; + vector x = position(initGrid,i); + vector p(x); + for (p[0]=x[0]-1; p[0]<=x[0]+1; p[0]++) { + set(initGrid(p),0) = 1.0; + set(initGrid(p),1) = 0.0; + set(initGrid(p),2) = 0.0; } } @@ -48,12 +49,13 @@ void generate(int dim, const char* filename) for (int j=0; j<50; j++) { int i = rand()%nodes(initGrid); - vector p = position(initGrid,i); - for (int x=p[0]-1; x<=p[0]+1; x++) - for (int y=p[1]-1; y<=p[1]+1; y++) { - set(initGrid[x][y],0) = 1.0; - set(initGrid[x][y],1) = 0.0; - set(initGrid[x][y],2) = 0.0; + vector x = position(initGrid,i); + vector p(x); + for (p[0]=x[0]-1; p[0]<=x[0]+1; p[0]++) + for (p[1]=x[1]-1; p[1]<=x[1]+1; p[1]++) { + set(initGrid(p),0) = 1.0; + set(initGrid(p),1) = 0.0; + set(initGrid(p),2) = 0.0; } } @@ -72,13 +74,14 @@ void generate(int dim, const char* filename) for (int j=0; j<50; j++) { int i = rand()%nodes(initGrid); - vector p = position(initGrid,i); - for (int x=p[0]-1; x<=p[0]+1; x++) - for (int y=p[1]-1; y<=p[1]+1; y++) - for (int z=p[1]-1; z<=p[2]+1; z++) { - set(initGrid[x][y][z],0) = 1.0; - set(initGrid[x][y][z],1) = 0.0; - set(initGrid[x][y][z],2) = 0.0; + vector x = position(initGrid,i); + vector p(x); + for (p[0]=x[0]-1; p[0]<=x[0]+1; p[0]++) + for (p[1]=x[1]-1; p[1]<=x[1]+1; p[1]++) + for (p[2]=x[2]-1; p[2]<=x[2]+1; p[2]++) { + set(initGrid(p),0) = 1.0; + set(initGrid(p),1) = 0.0; + set(initGrid(p),2) = 0.0; } } diff --git a/examples/coarsening/zener_pinning/isotropic/Monte_Carlo/zener.cpp b/examples/coarsening/zener_pinning/isotropic/Monte_Carlo/zener.cpp index b286a3a..6adbcdd 100644 --- a/examples/coarsening/zener_pinning/isotropic/Monte_Carlo/zener.cpp +++ b/examples/coarsening/zener_pinning/isotropic/Monte_Carlo/zener.cpp @@ -91,27 +91,27 @@ void generate(int dim, const char* filename) } } -template void update(MMSP::grid<1,T>& grid, int steps) +template void update(grid& spinGrid, int steps) { - const double kT = 0.50; - int gss = int(sqrt(nodes(grid))); + const double kT = (dim==3)?0.75:0.50; + int gss = int(sqrt(nodes(spinGrid))); for (int step=0; step x = position(grid,p); + vector x = position(spinGrid,p); // determine neighboring spins sparse neighbors; - set(neighbors,grid(x)) = true; + set(neighbors,spinGrid(x)) = true; for (int d=0; d<1; d++) { x[d]--; - set(neighbors,grid(x)) = true; + set(neighbors,spinGrid(x)) = true; x[d]+=2; - set(neighbors,grid(x)) = true; + set(neighbors,spinGrid(x)) = true; x[d]--; } @@ -121,113 +121,48 @@ template void update(MMSP::grid<1,T>& grid, int steps) if (spin1!=spin2 and spin2!=0) { // compute energy change double dE = -1.0; - for (int i=-1; i<=1; i++) { - int spin = grid[x[0]+i]; - dE += (spin!=spin2)-(spin!=spin1); - } - - // attempt a spin flip - double r = double(rand())/double(RAND_MAX); - if (dE<=0.0) grid(p) = spin2; - else if (r void update(MMSP::grid<2,T>& grid, int steps) -{ - const double kT = 0.50; - int gss = int(sqrt(nodes(grid))); - - for (int step=0; step x = position(grid,p); - // determine neighboring spins - sparse neighbors; - set(neighbors,grid(x)) = true; - for (int d=0; d<2; d++) { - x[d]--; - set(neighbors,grid(x)) = true; - x[d]+=2; - set(neighbors,grid(x)) = true; - x[d]--; - } - - // choose a random neighbor spin - int spin2 = index(neighbors,rand()%length(neighbors)); - - if (spin1!=spin2 and spin2!=0) { - // compute energy change - double dE = -1.0; - for (int i=-1; i<=1; i++) - for (int j=-1; j<=1; j++) { - int spin = grid[x[0]+i][x[1]+j]; + if (dim==1) { + for (int i=-1; i<=1; i++) { + x[0] += i; + int spin = spinGrid(x); dE += (spin!=spin2)-(spin!=spin1); + x[0] -= i; } - - // attempt a spin flip - double r = double(rand())/double(RAND_MAX); - if (dE<=0.0) grid(p) = spin2; - else if (r void update(MMSP::grid<3,T>& grid, int steps) -{ - const double kT = 0.75; - int gss = int(sqrt(nodes(grid))); - - for (int step=0; step x = position(grid,p); - // determine neighboring spins - sparse neighbors; - set(neighbors,grid(x)) = true; - for (int d=0; d<3; d++) { - x[d]--; - set(neighbors,grid(x)) = true; - x[d]+=2; - set(neighbors,grid(x)) = true; - x[d]--; - } - - // choose a random neighbor spin - int spin2 = index(neighbors,rand()%length(neighbors)); - - if (spin1!=spin2 and spin2!=0) { - // compute energy change - double dE = -1.0; - for (int i=-1; i<=1; i++) - for (int j=-1; j<=1; j++) - for (int k=-1; k<=1; k++) { - int spin = grid[x[0]+i][x[1]+j][x[2]+k]; + } else if (dim==2) { + for (int i=-1; i<=1; i++) { + x[0] += i; + for (int j=-1; j<=1; j++) { + x[1] += j; + int spin = spinGrid(x); dE += (spin!=spin2)-(spin!=spin1); + x[1] -= j; } + x[0] -= i; + } + } else if (dim==3) { + for (int i=-1; i<=1; i++) { + x[0] += i; + for (int j=-1; j<=1; j++) { + x[1] += j; + for (int k=-1; k<=1; k++) { + x[2] += k; + int spin = spinGrid(x); + dE += (spin!=spin2)-(spin!=spin1); + x[2] -= k; + } + x[1] -= j; + } + x[0] -= i; + } + } // attempt a spin flip double r = double(rand())/double(RAND_MAX); - if (dE<=0.0) grid(p) = spin2; - else if (r p = position(initGrid,i); - for (int x=p[0]-1; x<=p[0]+1; x++) { - initGrid[x][0] = 1.0; - initGrid[x][1] = 0.0; - initGrid[x][2] = 0.0; + vector x = position(initGrid,i); + vector p(x); + for (p[0]=x[0]-1; p[0]<=x[0]+1; p[0]++) { + initGrid(p)[0] = 1.0; + initGrid(p)[1] = 0.0; + initGrid(p)[2] = 0.0; } } @@ -51,12 +52,13 @@ void generate(int dim, const char* filename) for (int j=0; j<50; j++) { int i = rand()%nodes(initGrid); - vector p = position(initGrid,i); - for (int x=p[0]-1; x<=p[0]+1; x++) - for (int y=p[1]-1; y<=p[1]+1; y++) { - initGrid[x][y][0] = 1.0; - initGrid[x][y][1] = 0.0; - initGrid[x][y][2] = 0.0; + vector x = position(initGrid,i); + vector p(x); + for (p[0]=x[0]-1; p[0]<=x[0]+1; p[0]++) + for (p[1]=x[1]-1; p[1]<=x[1]+1; p[1]++) { + initGrid(p)[0] = 1.0; + initGrid(p)[1] = 0.0; + initGrid(p)[2] = 0.0; } } @@ -77,13 +79,14 @@ void generate(int dim, const char* filename) for (int j=0; j<50; j++) { int i = rand()%nodes(initGrid); - vector p = position(initGrid,i); - for (int x=p[0]-1; x<=p[0]+1; x++) - for (int y=p[1]-1; y<=p[1]+1; y++) - for (int z=p[1]-1; z<=p[2]+1; z++) { - initGrid[x][y][z][0] = 1.0; - initGrid[x][y][z][1] = 0.0; - initGrid[x][y][z][2] = 0.0; + vector x = position(initGrid,i); + vector p(x); + for (p[0]=x[0]-1; p[0]<=x[0]+1; p[0]++) + for (p[1]=x[1]-1; p[1]<=x[1]+1; p[1]++) + for (p[2]=x[2]-1; p[2]<=x[2]+1; p[2]++) { + initGrid(p)[0] = 1.0; + initGrid(p)[1] = 0.0; + initGrid(p)[2] = 0.0; } } diff --git a/examples/coarsening/zener_pinning/isotropic/sparsePF/zener.cpp b/examples/coarsening/zener_pinning/isotropic/sparsePF/zener.cpp index b133082..9edbc29 100644 --- a/examples/coarsening/zener_pinning/isotropic/sparsePF/zener.cpp +++ b/examples/coarsening/zener_pinning/isotropic/sparsePF/zener.cpp @@ -24,11 +24,12 @@ void generate(int dim, const char* filename) for (int j=0; j<50; j++) { int i = rand()%nodes(initGrid); - vector p = position(initGrid,i); - for (int x=p[0]-1; x<=p[0]+1; x++) { - set(initGrid[x],0) = 1.0; - set(initGrid[x],1) = 0.0; - set(initGrid[x],2) = 0.0; + vector x = position(initGrid,i); + vector p(x); + for (p[0]=x[0]-1; p[0]<=x[0]+1; p[0]++) { + set(initGrid(p),0) = 1.0; + set(initGrid(p),1) = 0.0; + set(initGrid(p),2) = 0.0; } } @@ -47,12 +48,13 @@ void generate(int dim, const char* filename) for (int j=0; j<50; j++) { int i = rand()%nodes(initGrid); - vector p = position(initGrid,i); - for (int x=p[0]-1; x<=p[0]+1; x++) - for (int y=p[1]-1; y<=p[1]+1; y++) { - set(initGrid[x][y],0) = 1.0; - set(initGrid[x][y],1) = 0.0; - set(initGrid[x][y],2) = 0.0; + vector x = position(initGrid,i); + vector p(x); + for (p[0]=x[0]-1; p[0]<=x[0]+1; p[0]++) + for (p[1]=x[1]-1; p[1]<=x[1]+1; p[1]++) { + set(initGrid(p),0) = 1.0; + set(initGrid(p),1) = 0.0; + set(initGrid(p),2) = 0.0; } } @@ -71,13 +73,14 @@ void generate(int dim, const char* filename) for (int j=0; j<50; j++) { int i = rand()%nodes(initGrid); - vector p = position(initGrid,i); - for (int x=p[0]-1; x<=p[0]+1; x++) - for (int y=p[1]-1; y<=p[1]+1; y++) - for (int z=p[1]-1; z<=p[2]+1; z++) { - set(initGrid[x][y][z],0) = 1.0; - set(initGrid[x][y][z],1) = 0.0; - set(initGrid[x][y][z],2) = 0.0; + vector x = position(initGrid,i); + vector p(x); + for (p[0]=x[0]-1; p[0]<=x[0]+1; p[0]++) + for (p[1]=x[1]-1; p[1]<=x[1]+1; p[1]++) + for (p[2]=x[2]-1; p[2]<=x[2]+1; p[2]++) { + set(initGrid(p),0) = 1.0; + set(initGrid(p),1) = 0.0; + set(initGrid(p),2) = 0.0; } } From 26c8668bb57eecd558bbf93e45343253b01a8610 Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Tue, 2 Feb 2016 14:22:42 -0500 Subject: [PATCH 10/83] progress bars for everyone --- .../anisotropic/Monte_Carlo/graingrowth.cpp | 12 +++++++----- .../anisotropic/phase_field/graingrowth.cpp | 6 ++++++ .../anisotropic/sparsePF/graingrowth.cpp | 6 ++++++ .../isotropic/Monte_Carlo/graingrowth.cpp | 4 +++- .../isotropic/phase_field/graingrowth.cpp | 8 +++++++- .../grain_growth/isotropic/sparsePF/graingrowth.cpp | 7 +++++++ .../isotropic/phase_field/ostwald.cpp | 7 +++++++ .../zener_pinning/anisotropic/Monte_Carlo/zener.cpp | 8 ++++++++ .../zener_pinning/anisotropic/phase_field/zener.cpp | 7 +++++++ .../zener_pinning/anisotropic/sparsePF/zener.cpp | 8 ++++++++ .../zener_pinning/isotropic/Monte_Carlo/zener.cpp | 8 ++++++++ .../zener_pinning/isotropic/phase_field/zener.cpp | 8 ++++++++ .../zener_pinning/isotropic/sparsePF/zener.cpp | 8 ++++++++ examples/phase_transitions/allen-cahn/allen-cahn.cpp | 8 ++++++++ .../cahn-hilliard/convex_splitting/cahn-hilliard.cpp | 3 +++ .../cahn-hilliard/explicit/cahn-hilliard.cpp | 8 ++++++++ examples/phase_transitions/model_A/model_A.cpp | 8 ++++++++ examples/phase_transitions/model_B/model_B.cpp | 8 ++++++++ examples/phase_transitions/spinodal/spinodal.cpp | 8 ++++++++ .../statistical_mechanics/Heisenberg/heisenberg.cpp | 8 ++++++++ examples/statistical_mechanics/Ising/ising.cpp | 8 ++++++++ examples/statistical_mechanics/Potts/potts.cpp | 8 ++++++++ 22 files changed, 157 insertions(+), 7 deletions(-) diff --git a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp index 8b745a3..226ebaa 100644 --- a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp +++ b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp @@ -68,7 +68,7 @@ template void update(grid& mcGrid, int steps) MPI::COMM_WORLD.Barrier(); #endif -/*---------------generate cells------------------*/ + /*---------------generate cells------------------*/ int dimension_length=0, number_of_lattice_cells=1; int lattice_cells_each_dimension[dim]; for(int i=0; i void update(grid& mcGrid, int steps) } for(int step=0; step x (dim,0); diff --git a/examples/coarsening/grain_growth/anisotropic/phase_field/graingrowth.cpp b/examples/coarsening/grain_growth/anisotropic/phase_field/graingrowth.cpp index 9b32293..4c62edc 100644 --- a/examples/coarsening/grain_growth/anisotropic/phase_field/graingrowth.cpp +++ b/examples/coarsening/grain_growth/anisotropic/phase_field/graingrowth.cpp @@ -85,12 +85,18 @@ void generate(int dim, const char* filename) template void update(grid >& oldGrid, int steps) { + int rank=0; + #ifdef MPI_VERSION + rank = MPI::COMM_WORLD.Get_rank(); + #endif grid > newGrid(oldGrid); double dt = 0.01; double width = 8.0; for (int step=0; step x = position(oldGrid,i); diff --git a/examples/coarsening/grain_growth/anisotropic/sparsePF/graingrowth.cpp b/examples/coarsening/grain_growth/anisotropic/sparsePF/graingrowth.cpp index 38f1e1e..f39d16c 100644 --- a/examples/coarsening/grain_growth/anisotropic/sparsePF/graingrowth.cpp +++ b/examples/coarsening/grain_growth/anisotropic/sparsePF/graingrowth.cpp @@ -76,11 +76,17 @@ void generate(int dim, const char* filename) template void update(grid >& oldGrid, int steps) { + int rank=0; + #ifdef MPI_VERSION + rank = MPI::COMM_WORLD.Get_rank(); + #endif double dt = 0.01; double width = 8.0; double epsilon = 1.0e-8; for (int step=0; step > newGrid(oldGrid); diff --git a/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp b/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp index fca5238..8581b68 100644 --- a/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp +++ b/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp @@ -61,7 +61,7 @@ template void update(grid& mcGrid, int steps) MPI::COMM_WORLD.Barrier(); #endif -/*---------------generate cells------------------*/ + /*---------------generate cells------------------*/ int dimension_length=0, number_of_lattice_cells=1; int lattice_cells_each_dimension[dim]; for(int i=0; i void update(grid& mcGrid, int steps) } for(int step=0; step void update(grid >& oldGrid, int steps) { - grid > newGrid(oldGrid); + int rank=0; + #ifdef MPI_VERSION + rank = MPI::COMM_WORLD.Get_rank(); + #endif + grid > newGrid(oldGrid); double dt = 0.01; for (int step=0; step lap = laplacian(oldGrid,i); diff --git a/examples/coarsening/grain_growth/isotropic/sparsePF/graingrowth.cpp b/examples/coarsening/grain_growth/isotropic/sparsePF/graingrowth.cpp index 7b0a99f..87642cf 100644 --- a/examples/coarsening/grain_growth/isotropic/sparsePF/graingrowth.cpp +++ b/examples/coarsening/grain_growth/isotropic/sparsePF/graingrowth.cpp @@ -54,10 +54,17 @@ void generate(int dim, const char* filename) template void update(grid >& oldGrid, int steps) { + int rank=0; + #ifdef MPI_VERSION + rank = MPI::COMM_WORLD.Get_rank(); + #endif + double dt = 0.01; double epsilon = 1.0e-8; for (int step=0; step > newGrid(oldGrid); diff --git a/examples/coarsening/ostwald_ripening/isotropic/phase_field/ostwald.cpp b/examples/coarsening/ostwald_ripening/isotropic/phase_field/ostwald.cpp index 70b3c8a..fce3ae6 100644 --- a/examples/coarsening/ostwald_ripening/isotropic/phase_field/ostwald.cpp +++ b/examples/coarsening/ostwald_ripening/isotropic/phase_field/ostwald.cpp @@ -51,6 +51,11 @@ void generate(int dim, const char* filename) template void update(grid >& oldGrid, int steps) { + int rank=0; + #ifdef MPI_VERSION + rank = MPI::COMM_WORLD.Get_rank(); + #endif + grid > newGrid(oldGrid); grid wspace(oldGrid,1); @@ -72,6 +77,8 @@ template void update(grid >& oldGrid, int st for (int step=0; step x = position(oldGrid, n); double sum = 0.0; diff --git a/examples/coarsening/zener_pinning/anisotropic/Monte_Carlo/zener.cpp b/examples/coarsening/zener_pinning/anisotropic/Monte_Carlo/zener.cpp index 1f79492..7d9fed6 100644 --- a/examples/coarsening/zener_pinning/anisotropic/Monte_Carlo/zener.cpp +++ b/examples/coarsening/zener_pinning/anisotropic/Monte_Carlo/zener.cpp @@ -82,10 +82,18 @@ void generate(int dim, const char* filename) template void update(grid& mcGrid, int steps) { + int rank=0; + #ifdef MPI_VERSION + rank = MPI::COMM_WORLD.Get_rank(); + #endif + const double kT = (dim==3)?0.75:0.50; int gss = int(nodes(mcGrid)); for (int step=0; step void update(grid >& oldGrid, int steps) { + int rank=0; + #ifdef MPI_VERSION + rank = MPI::COMM_WORLD.Get_rank(); + #endif + grid > newGrid(oldGrid); double dt = 0.01; double width = 8.0; for (int step=0; step void update(grid >& oldGrid, int steps) { + int rank=0; + #ifdef MPI_VERSION + rank = MPI::COMM_WORLD.Get_rank(); + #endif + double dt = 0.01; double width = 8.0; double epsilon = 1.0e-8; for (int step=0; step > newGrid(oldGrid); for (int n=0; n void update(grid& spinGrid, int steps) { + int rank=0; + #ifdef MPI_VERSION + rank = MPI::COMM_WORLD.Get_rank(); + #endif + const double kT = (dim==3)?0.75:0.50; int gss = int(sqrt(nodes(spinGrid))); for (int step=0; step void update(grid >& oldGrid, int steps) { + int rank=0; + #ifdef MPI_VERSION + rank = MPI::COMM_WORLD.Get_rank(); + #endif + grid > newGrid(oldGrid); double dt = 0.01; for (int step=0; step lap = laplacian(oldGrid,i); diff --git a/examples/coarsening/zener_pinning/isotropic/sparsePF/zener.cpp b/examples/coarsening/zener_pinning/isotropic/sparsePF/zener.cpp index 9edbc29..57fb549 100644 --- a/examples/coarsening/zener_pinning/isotropic/sparsePF/zener.cpp +++ b/examples/coarsening/zener_pinning/isotropic/sparsePF/zener.cpp @@ -90,10 +90,18 @@ void generate(int dim, const char* filename) template void update(grid >& oldGrid, int steps) { + int rank=0; + #ifdef MPI_VERSION + rank = MPI::COMM_WORLD.Get_rank(); + #endif + double dt = 0.01; double epsilon = 1.0e-8; for (int step=0; step > newGrid(oldGrid); diff --git a/examples/phase_transitions/allen-cahn/allen-cahn.cpp b/examples/phase_transitions/allen-cahn/allen-cahn.cpp index ffff406..26bd764 100644 --- a/examples/phase_transitions/allen-cahn/allen-cahn.cpp +++ b/examples/phase_transitions/allen-cahn/allen-cahn.cpp @@ -42,6 +42,11 @@ void generate(int dim, const char* filename) template void update(grid& oldGrid, int steps) { + int rank=0; + #ifdef MPI_VERSION + rank = MPI::COMM_WORLD.Get_rank(); + #endif + grid newGrid(oldGrid); double r = 1.0; @@ -51,6 +56,9 @@ template void update(grid& oldGrid, int steps) double dt = 0.01; for (int step=0; step >& oldGrid, int steps) #endif for (int step=0; step void update(grid& oldGrid, int steps) { + int rank=0; + #ifdef MPI_VERSION + rank = MPI::COMM_WORLD.Get_rank(); + #endif + grid newGrid(oldGrid); grid temp(oldGrid); @@ -52,6 +57,9 @@ template void update(grid& oldGrid, int steps) double dt = 0.01; for (int step=0; step void update(grid& oldGrid, int steps) { + int rank=0; + #ifdef MPI_VERSION + rank = MPI::COMM_WORLD.Get_rank(); + #endif + grid newGrid(oldGrid); T r = 1.0; @@ -80,6 +85,9 @@ template void update(grid& oldGrid, int steps) T dV = 1.0; for (int step=0; step void update(grid& oldGrid, int steps) { + int rank=0; + #ifdef MPI_VERSION + rank = MPI::COMM_WORLD.Get_rank(); + #endif + grid newGrid(oldGrid); grid temp(oldGrid); @@ -81,6 +86,9 @@ template void update(grid& oldGrid, int steps) T dV = 1.0; for (int step=0; step void update(grid& oldGrid, int steps) { + int rank=0; + #ifdef MPI_VERSION + rank = MPI::COMM_WORLD.Get_rank(); + #endif + grid newGrid(oldGrid); grid temp(oldGrid); @@ -77,6 +82,9 @@ template void update(grid& oldGrid, int steps) T epsilon = 0.05; for (int step=0; step void update(grid >& spinGrid, int steps) { + int rank=0; + #ifdef MPI_VERSION + rank = MPI::COMM_WORLD.Get_rank(); + #endif + double J = 1.0; double kT = (dim==3)?0.75:0.50; double pi = acos(-1.0); @@ -64,6 +69,9 @@ template void update(grid >& spinGrid, int s int gss = (dim==1)?nodes(spinGrid):int(sqrt(nodes(spinGrid))); for (int step=0; step void update(grid& spinGrid, int steps) { + int rank=0; + #ifdef MPI_VERSION + rank = MPI::COMM_WORLD.Get_rank(); + #endif + double J = 2.0; double H = 1.0; double kT = (dim==2)?0.50:0.75; @@ -49,6 +54,9 @@ template void update(grid& spinGrid, int steps) int gss = (dim==1)?nodes(spinGrid):int(sqrt(nodes(spinGrid))); for (int step=0; step void update(grid& spinGrid, int steps) { + int rank=0; + #ifdef MPI_VERSION + rank = MPI::COMM_WORLD.Get_rank(); + #endif + int Q = 20; double J = 1.0; double kT = (dim==3)?0.75:0.50; @@ -49,6 +54,9 @@ template void update(grid& spinGrid, int steps) int gss = (dim==1)?nodes(spinGrid):int(sqrt(nodes(spinGrid))); for (int step=0; step Date: Tue, 2 Feb 2016 14:47:41 -0500 Subject: [PATCH 11/83] harmonize formatting (spaces to tabs, pad parens) --- .../anisotropic/Monte_Carlo/graingrowth.cpp | 644 +++++++++--------- .../isotropic/Monte_Carlo/graingrowth.cpp | 629 +++++++++-------- 2 files changed, 665 insertions(+), 608 deletions(-) diff --git a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp index 226ebaa..267008d 100644 --- a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp +++ b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp @@ -1,9 +1,11 @@ -// graingrowth.cpp -// Algorithms for 2D and 3D isotropic Monte Carlo grain growth -// Parallel algorithm: Wright, Steven A., et al. "Potts-model grain growth simulations: Parallel algorithms and applications." SAND Report (1997): 1925. -// Ghost communiation is performed on 1/4 of all boundaries each time. -// Temperature dependence comes from: Godfrey, A. W., and J. W. Martin. "Some Monte Carlo studies of grain growth in a temperature gradient." Philosophical Magazine A 72.3 (1995): 737-749. -// Questions/comments to tany3@rpi.edu (Yixuan Tan) +/* graingrowth.cpp +** Algorithms for 2D and 3D isotropic Monte Carlo grain growth +** Ghost communiation is performed on 1/4 of all boundaries each time. +** Parallel algorithm: Wright, Steven A., et al. "Potts-model grain growth simulations: Parallel algorithms and applications." SAND Report (1997): 1925. +** Temperature dependence: Godfrey, A. W., and J. W. Martin. "Some Monte Carlo studies of grain growth in a temperature gradient." Philosophical Magazine A 72.3 (1995): 737-749. +** +** Questions/comments to tany3@rpi.edu (Yixuan Tan) +*/ #ifndef GRAINGROWTH_UPDATE #define GRAINGROWTH_UPDATE @@ -16,327 +18,353 @@ namespace MMSP { void generate(int dim, const char* filename) { - if (dim==1) { - GRID1D initGrid(0,0,128); - - for (int i=0; i x = position(initGrid,i); - if (x[0]<32) initGrid(i) = 3; - else if (x[0]>96) initGrid(i) = 2; - else initGrid(i) = 0; + if (dim == 1) { + GRID1D initGrid(0, 0, 128); + + for (int i = 0; i < nodes(initGrid); i++) { + vector x = position(initGrid, i); + if (x[0] < 32) initGrid(i) = 3; + else if (x[0] > 96) initGrid(i) = 2; + else initGrid(i) = 0; } - output(initGrid,filename); + output(initGrid, filename); } - if (dim==2) { - GRID2D initGrid(2,0,64,0,64); + if (dim == 2) { + GRID2D initGrid(2, 0, 64, 0, 64); - for (int i=0; i bool OutsideDomainCheck(grid& mcGrid, vector* x){ - bool outside_domain=false; - for(int i=0; ix1(mcGrid, i)){ - outside_domain=true; - break; - } - } - return outside_domain; +template bool OutsideDomainCheck(grid& mcGrid, vector* x) +{ + bool outside_domain = false; + for (int i = 0; i < dim; i++) { + if ((*x)[i] < x0(mcGrid, i) || (*x)[i] > x1(mcGrid, i)) { + outside_domain = true; + break; + } + } + return outside_domain; } -template void update(grid& mcGrid, int steps) +template void update(grid& mcGrid, int steps) { - int rank=0; - unsigned int np=0; + int rank = 0; + unsigned int np = 0; #ifdef MPI_VERSION - rank=MPI::COMM_WORLD.Get_rank(); - np=MPI::COMM_WORLD.Get_size(); + rank = MPI::COMM_WORLD.Get_rank(); + np = MPI::COMM_WORLD.Get_size(); MPI::COMM_WORLD.Barrier(); #endif - /*---------------generate cells------------------*/ - int dimension_length=0, number_of_lattice_cells=1; - int lattice_cells_each_dimension[dim]; - for(int i=0; i x (dim,0); - vector x_prim (dim,0); - int coordinates_of_cell[dim]; - int initial_coordinates[dim]; - - int num_grids_to_flip[( static_cast(pow(2,dim)) )]; - int first_cell_start_coordinates[dim]; - for(int kk=0; kk(mcGrid, &x_prim)) num_grids_to_flip[0]+=1; - - x_prim = x; - x_prim[1]=x[1]+1; //0,1 - if(!OutsideDomainCheck(mcGrid, &x_prim)) num_grids_to_flip[1]+=1; - - x_prim = x; - x_prim[0]=x[0]+1; //1,0 - if(!OutsideDomainCheck(mcGrid, &x_prim)) num_grids_to_flip[2]+=1; - - x_prim = x; - x_prim[0]=x[0]+1; - x_prim[1]=x[1]+1; //1,1 - if(!OutsideDomainCheck(mcGrid, &x_prim)) num_grids_to_flip[3]+=1; - - }else if(dim==3){ - x_prim = x;//0,0,0 - if(!OutsideDomainCheck(mcGrid, &x_prim)) num_grids_to_flip[0]+=1; - - x_prim = x; - x_prim[2]=x[2]+1; //0,0,1 - if(!OutsideDomainCheck(mcGrid, &x_prim)) num_grids_to_flip[1]+=1; - - x_prim = x; - x_prim[1]=x[1]+1; //0,1,0 - if(!OutsideDomainCheck(mcGrid, &x_prim)) num_grids_to_flip[2]+=1; - - x_prim = x; - x_prim[2]=x[2]+1; - x_prim[1]=x[1]+1; //0,1,1 - if(!OutsideDomainCheck(mcGrid, &x_prim)) num_grids_to_flip[3]+=1; - - x_prim = x; - x_prim[0]=x[0]+1; //1,0,0 - if(!OutsideDomainCheck(mcGrid, &x_prim)) num_grids_to_flip[4]+=1; - - x_prim = x; - x_prim[2]=x[2]+1; - x_prim[0]=x[0]+1; //1,0,1 - if(!OutsideDomainCheck(mcGrid, &x_prim)) num_grids_to_flip[5]+=1; - - x_prim = x; - x_prim[1]=x[1]+1; - x_prim[0]=x[0]+1; //1,1,0 - if(!OutsideDomainCheck(mcGrid, &x_prim)) num_grids_to_flip[6]+=1; - - x_prim = x; - x_prim[2]=x[2]+1; - x_prim[1]=x[1]+1; - x_prim[0]=x[0]+1; //1,1,1 - if(!OutsideDomainCheck(mcGrid, &x_prim)) num_grids_to_flip[7]+=1; - - } - }// for int j - - for(int k=0; k x (dim,0); - - for (int hh=0; hh=x1(mcGrid, i)){ -// if(x[i]x1(mcGrid, i)-1){ - site_out_of_domain = true; - break;//break from the for int i loop - } - } - if(site_out_of_domain == true){ - hh--; - continue; //continue the int hh loop - } - - double Q = 1.0e5; - double R = 8.314; - double T = 673 + 50.0/(g1(mcGrid, 0)-g0(mcGrid, 0))*(x[0]-g0(mcGrid, 0)); // temperature is 673K at one half and 723K at the other half - double site_selection_probability = exp(-Q/R/T)/exp(-Q/R/723); - - double rd = double(rand())/double(RAND_MAX); - if(rd>site_selection_probability) continue;//this site wont be selected - - int spin1 = (mcGrid)(x); - // determine neighboring spinsss - vector r(dim,0); - std::vector neighbors; - neighbors.clear(); - unsigned int number_of_same_neighours = 0; - if(dim==2){ - for (int i=-1; i<=1; i++) { - for (int j=-1; j<=1; j++) { - if(!(i==0 && j==0)){ - r[0] = x[0] + i; - r[1] = x[1] + j; - if(r[0]=g1(mcGrid, 0) || r[1]=g1(mcGrid, 1) )// not periodic BC - continue;// neighbour outside the global boundary, skip it. - int spin = (mcGrid)(r); - neighbors.push_back(spin); - if(spin==spin1) - number_of_same_neighours++; - } - } - } - }else if(dim==3){ - for (int i=-1; i<=1; i++){ - for (int j=-1; j<=1; j++){ - for (int k=-1; k<=1; k++) { - if(!(i==0 && j==0 && k==0)){ - r[0] = x[0] + i; - r[1] = x[1] + j; - r[2] = x[2] + k; - if(r[0]=g1(mcGrid, 0) || r[1]=g1(mcGrid, 1) || - r[2]=g1(mcGrid, 2))// not periodic BC - continue;// neighbour outside the global boundary, skip it. - int spin = (mcGrid)(r); - neighbors.push_back(spin); - if(spin==spin1) - number_of_same_neighours++; - } - } - } - } - } - - //check if inside a grain - if(number_of_same_neighours==neighbors.size()){//inside a grain - continue;//continue for - } - //choose a random neighbor spin - int spin2 = neighbors[rand()%neighbors.size()]; - // choose a random spin from Q states - if(spin1!=spin2){ - // compute energy change - double dE = 0.0; - if(dim==2){ - for (int i=-1; i<=1; i++){ - for (int j=-1; j<=1; j++){ - if(!(i==0 && j==0)){ - r[0] = x[0] + i; - r[1] = x[1] + j; - if(r[0]=g1(mcGrid, 0) || r[1]=g1(mcGrid, 1) ) // not periodic BC - continue;// neighbour outside the global boundary, skip it. - int spin = (mcGrid)(r); - dE += 1.0/2*((spin!=spin2)-(spin!=spin1)); - }// if(!(i==0 && j==0)) - } - } - } - if(dim==3){ - for (int i=-1; i<=1; i++){ - for (int j=-1; j<=1; j++){ - for (int k=-1; k<=1; k++){ - if(!(i==0 && j==0 && k==0)){ - r[0] = x[0] + i; - r[1] = x[1] + j; - r[2] = x[2] + k; - if(r[0]=g1(mcGrid, 0) || r[1]=g1(mcGrid, 1) || - r[2]=g1(mcGrid, 2)) // not periodic BC - continue;// neighbour outside the global boundary, skip it. - int spin = (mcGrid)(r); - dE += 1.0/2*(spin!=spin2)-(spin!=spin1); - } - } - } - } - } - // attempt a spin flip - double r = double(rand())/double(RAND_MAX); - double kT = 1.3803288e-23*273; - if (dE <= 0.0) (mcGrid)(x) = spin2; - else if (r x (dim, 0); + vector x_prim (dim, 0); + int coordinates_of_cell[dim]; + int initial_coordinates[dim]; + + int num_grids_to_flip[( static_cast(pow(2, dim)) )]; + int first_cell_start_coordinates[dim]; + for (int kk = 0; kk < dim; kk++) first_cell_start_coordinates[kk] = x0(mcGrid, kk); + for (int i = 0; i < dim; i++) { + if (x0(mcGrid, i) % 2 != 0) first_cell_start_coordinates[i]--; + } + + + for (int j = 0; j < number_of_lattice_cells; j++) { + int cell_coords_selected[dim]; + if (dim == 2) { + cell_coords_selected[dim - 1] = j % lattice_cells_each_dimension[dim - 1]; //1-indexed + cell_coords_selected[0] = (j / lattice_cells_each_dimension[dim - 1]); + } else if (dim == 3) { + cell_coords_selected[dim - 1] = j % lattice_cells_each_dimension[dim - 1]; //1-indexed + cell_coords_selected[1] = (j / lattice_cells_each_dimension[dim - 1]) % lattice_cells_each_dimension[1]; + cell_coords_selected[0] = ( j / lattice_cells_each_dimension[dim - 1] ) / lattice_cells_each_dimension[1]; + } + for (int i = 0; i < dim; i++) { + x[i] = first_cell_start_coordinates[i] + 2 * cell_coords_selected[i]; + } + + if (dim == 2) { + x_prim = x; + if (!OutsideDomainCheck(mcGrid, &x_prim)) num_grids_to_flip[0] += 1; + + x_prim = x; + x_prim[1] = x[1] + 1; //0,1 + if (!OutsideDomainCheck(mcGrid, &x_prim)) num_grids_to_flip[1] += 1; + + x_prim = x; + x_prim[0] = x[0] + 1; //1,0 + if (!OutsideDomainCheck(mcGrid, &x_prim)) num_grids_to_flip[2] += 1; + + x_prim = x; + x_prim[0] = x[0] + 1; + x_prim[1] = x[1] + 1; //1,1 + if (!OutsideDomainCheck(mcGrid, &x_prim)) num_grids_to_flip[3] += 1; + + } else if (dim == 3) { + x_prim = x;//0,0,0 + if (!OutsideDomainCheck(mcGrid, &x_prim)) num_grids_to_flip[0] += 1; + + x_prim = x; + x_prim[2] = x[2] + 1; //0,0,1 + if (!OutsideDomainCheck(mcGrid, &x_prim)) num_grids_to_flip[1] += 1; + + x_prim = x; + x_prim[1] = x[1] + 1; //0,1,0 + if (!OutsideDomainCheck(mcGrid, &x_prim)) num_grids_to_flip[2] += 1; + + x_prim = x; + x_prim[2] = x[2] + 1; + x_prim[1] = x[1] + 1; //0,1,1 + if (!OutsideDomainCheck(mcGrid, &x_prim)) num_grids_to_flip[3] += 1; + + x_prim = x; + x_prim[0] = x[0] + 1; //1,0,0 + if (!OutsideDomainCheck(mcGrid, &x_prim)) num_grids_to_flip[4] += 1; + + x_prim = x; + x_prim[2] = x[2] + 1; + x_prim[0] = x[0] + 1; //1,0,1 + if (!OutsideDomainCheck(mcGrid, &x_prim)) num_grids_to_flip[5] += 1; + + x_prim = x; + x_prim[1] = x[1] + 1; + x_prim[0] = x[0] + 1; //1,1,0 + if (!OutsideDomainCheck(mcGrid, &x_prim)) num_grids_to_flip[6] += 1; + + x_prim = x; + x_prim[2] = x[2] + 1; + x_prim[1] = x[1] + 1; + x_prim[0] = x[0] + 1; //1,1,1 + if (!OutsideDomainCheck(mcGrid, &x_prim)) num_grids_to_flip[7] += 1; + } + }// for int j + + for (int k = 0; k < dim; k++) + initial_coordinates[k] = x0(mcGrid, k); + for (int i = 0; i < dim; i++) { + if (x0(mcGrid, i) % 2 != 0) + initial_coordinates[i]--; + } + + for (int step = 0; step < steps; step++) { + if (rank == 0) + print_progress(step, steps); + int num_sublattices = 0; + if (dim == 2) num_sublattices = 4; + else if (dim == 3) num_sublattices = 8; + for (int sublattice = 0; sublattice < num_sublattices; sublattice++) { + + srand(time(NULL)); /* seed random number generator */ + vector x (dim, 0); + + for (int hh = 0; hh < num_grids_to_flip[sublattice]; hh++) { + int cell_numbering = rand() % (number_of_lattice_cells); //choose a cell to flip, from 0 to num_cells_in_thread-1 + int cell_coords_selected[dim]; + if (dim == 2) { + cell_coords_selected[dim - 1] = cell_numbering % lattice_cells_each_dimension[dim - 1]; //1-indexed + cell_coords_selected[0] = (cell_numbering / lattice_cells_each_dimension[dim - 1]); + } else if (dim == 3) { + cell_coords_selected[dim - 1] = cell_numbering % lattice_cells_each_dimension[dim - 1]; //1-indexed + cell_coords_selected[1] = (cell_numbering / lattice_cells_each_dimension[dim - 1]) % lattice_cells_each_dimension[1]; + cell_coords_selected[0] = ( cell_numbering / lattice_cells_each_dimension[dim - 1] ) / lattice_cells_each_dimension[1]; + } + for (int i = 0; i < dim; i++) { + x[i] = first_cell_start_coordinates[i] + 2 * cell_coords_selected[i]; + } + + if (dim == 2) { + switch(sublattice) { + case 0: + break;// 0,0 + case 1: + x[1]++; + break; //0,1 + case 2: + x[0]++; + break; //1,0 + case 3: + x[0]++; + x[1]++; + break; //1,1 + } + } else if (dim == 3) { + switch(sublattice) { + case 0: + break;// 0,0,0 + case 1: + x[2]++; + break; //0,0,1 + case 2: + x[1]++; + break; //0,1,0 + case 3: + x[2]++; + x[1]++; + break; //0,1,1 + case 4: + x[0]++; + break; //1,0,0 + case 5: + x[2]++; + x[0]++; + break; //1,0,1 + case 6: + x[1]++; + x[0]++; + break; //1,1,0 + case 7: + x[2]++; + x[1]++; + x[0]++; //1,1,1 + } + } + + bool site_out_of_domain = false; + for (int i = 0; i < dim; i++) { + if (x[i] < x0(mcGrid, i) || x[i] >= x1(mcGrid, i)) { +// if (x[i]x1(mcGrid, i)-1){ + site_out_of_domain = true; + break;//break from the for int i loop + } + } + if (site_out_of_domain == true) { + hh--; + continue; //continue the int hh loop + } + + double Q = 1.0e5; + double R = 8.314; + double T = 673 + 50.0 / (g1(mcGrid, 0) - g0(mcGrid, 0)) * (x[0] - g0(mcGrid, 0)); // temperature is 673K at one half and 723K at the other half + double site_selection_probability = exp(-Q / R / T) / exp(-Q / R / 723); + + double rd = double(rand()) / double(RAND_MAX); + if (rd > site_selection_probability) continue; //this site wont be selected + + int spin1 = (mcGrid)(x); + // determine neighboring spinsss + vector r(dim, 0); + std::vector neighbors; + neighbors.clear(); + unsigned int number_of_same_neighours = 0; + if (dim == 2) { + for (int i = -1; i <= 1; i++) { + for (int j = -1; j <= 1; j++) { + if (!(i == 0 && j == 0)) { + r[0] = x[0] + i; + r[1] = x[1] + j; + if (r[0] < g0(mcGrid, 0) || r[0] >= g1(mcGrid, 0) || r[1] < g0(mcGrid, 1) || r[1] >= g1(mcGrid, 1) ) // not periodic BC + continue;// neighbour outside the global boundary, skip it. + int spin = (mcGrid)(r); + neighbors.push_back(spin); + if (spin == spin1) + number_of_same_neighours++; + } + } + } + } else if (dim == 3) { + for (int i = -1; i <= 1; i++) { + for (int j = -1; j <= 1; j++) { + for (int k = -1; k <= 1; k++) { + if (!(i == 0 && j == 0 && k == 0)) { + r[0] = x[0] + i; + r[1] = x[1] + j; + r[2] = x[2] + k; + if (r[0] < g0(mcGrid, 0) || r[0] >= g1(mcGrid, 0) || r[1] < g0(mcGrid, 1) || r[1] >= g1(mcGrid, 1) || + r[2] < g0(mcGrid, 2) || r[2] >= g1(mcGrid, 2)) // not periodic BC + continue;// neighbour outside the global boundary, skip it. + int spin = (mcGrid)(r); + neighbors.push_back(spin); + if (spin == spin1) + number_of_same_neighours++; + } + } + } + } + } + + //check if inside a grain + if (number_of_same_neighours == neighbors.size()) { //inside a grain + continue;//continue for + } + //choose a random neighbor spin + int spin2 = neighbors[rand() % neighbors.size()]; + // choose a random spin from Q states + if (spin1 != spin2) { + // compute energy change + double dE = 0.0; + if (dim == 2) { + for (int i = -1; i <= 1; i++) { + for (int j = -1; j <= 1; j++) { + if (!(i == 0 && j == 0)) { + r[0] = x[0] + i; + r[1] = x[1] + j; + if (r[0] < g0(mcGrid, 0) || r[0] >= g1(mcGrid, 0) || r[1] < g0(mcGrid, 1) || r[1] >= g1(mcGrid, 1) ) // not periodic BC + continue;// neighbour outside the global boundary, skip it. + int spin = (mcGrid)(r); + dE += 1.0 / 2 * ((spin != spin2) - (spin != spin1)); + }// if (!(i==0 && j==0)) + } + } + } + if (dim == 3) { + for (int i = -1; i <= 1; i++) { + for (int j = -1; j <= 1; j++) { + for (int k = -1; k <= 1; k++) { + if (!(i == 0 && j == 0 && k == 0)) { + r[0] = x[0] + i; + r[1] = x[1] + j; + r[2] = x[2] + k; + if (r[0] < g0(mcGrid, 0) || r[0] >= g1(mcGrid, 0) || r[1] < g0(mcGrid, 1) || r[1] >= g1(mcGrid, 1) || + r[2] < g0(mcGrid, 2) || r[2] >= g1(mcGrid, 2)) // not periodic BC + continue;// neighbour outside the global boundary, skip it. + int spin = (mcGrid)(r); + dE += 1.0 / 2 * (spin != spin2) - (spin != spin1); + } + } + } + } + } + // attempt a spin flip + double r = double(rand()) / double(RAND_MAX); + double kT = 1.3803288e-23 * 273; + if (dE <= 0.0) (mcGrid)(x) = spin2; + else if (r < exp(-dE / kT)) (mcGrid)(x) = spin2; + }//spin1!=spin2 + } // hh + +#ifdef MPI_VERSION MPI::COMM_WORLD.Barrier(); - #endif - ghostswap(mcGrid,sublattice); // once looped over a "color", ghostswap. - #ifdef MPI_VERSION +#endif + ghostswap(mcGrid, sublattice); // once looped over a "color", ghostswap. +#ifdef MPI_VERSION MPI::COMM_WORLD.Barrier(); - #endif +#endif }//loop over sublattice }//loop over step }//update diff --git a/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp b/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp index 8581b68..e002f96 100644 --- a/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp +++ b/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp @@ -1,8 +1,10 @@ -// graingrowth.cpp -// Algorithms for 2D and 3D isotropic Monte Carlo grain growth -// Parallel algorithm: Wright, Steven A., et al. "Potts-model grain growth simulations: Parallel algorithms and applications." SAND Report (1997): 1925. -// Ghost communiation is performed on 1/4 of all boundaries each time. -// Questions/comments to tany3@rpi.edu (Yixuan Tan) +/* graingrowth.cpp +** Algorithms for 2D and 3D isotropic Monte Carlo grain growth +** Ghost communiation is performed on 1/4 of all boundaries each time. +** Parallel algorithm: Wright, Steven A., et al. "Potts-model grain growth simulations: Parallel algorithms and applications." SAND Report (1997): 1925. +** +** Questions/comments to tany3@rpi.edu (Yixuan Tan) +*/ #ifndef GRAINGROWTH_UPDATE #define GRAINGROWTH_UPDATE @@ -15,318 +17,345 @@ namespace MMSP { void generate(int dim, const char* filename) { - if (dim==1) { - GRID1D initGrid(0,0,128); + if (dim == 1) { + GRID1D initGrid(0, 0, 128); - for (int i=0; i bool OutsideDomainCheck(grid& mcGrid, vector* x){ - bool outside_domain=false; - for(int i=0; ix1(mcGrid, i)){ - outside_domain=true; - break; - } - } - return outside_domain; +template bool OutsideDomainCheck(grid& mcGrid, vector* x) +{ + bool outside_domain = false; + for (int i = 0; i < dim; i++) { + if ((*x)[i] < x0(mcGrid, i) || (*x)[i] > x1(mcGrid, i)) { + outside_domain = true; + break; + } + } + return outside_domain; } -template void update(grid& mcGrid, int steps) +template void update(grid& mcGrid, int steps) { - int rank=0; - unsigned int np=0; + int rank = 0; + unsigned int np = 0; #ifdef MPI_VERSION - rank=MPI::COMM_WORLD.Get_rank(); - np=MPI::COMM_WORLD.Get_size(); + rank = MPI::COMM_WORLD.Get_rank(); + np = MPI::COMM_WORLD.Get_size(); MPI::COMM_WORLD.Barrier(); #endif - /*---------------generate cells------------------*/ - int dimension_length=0, number_of_lattice_cells=1; - int lattice_cells_each_dimension[dim]; - for(int i=0; i x (dim,0); - vector x_prim (dim,0); - int coordinates_of_cell[dim]; - int initial_coordinates[dim]; - - int num_of_grids_to_flip[( static_cast(pow(2,dim)) )]; - int first_cell_start_coordinates[dim]; - for(int kk=0; kk(mcGrid, &x_prim)) num_of_grids_to_flip[0]+=1; - - x_prim = x; - x_prim[1]=x[1]+1; //0,1 - if(!OutsideDomainCheck(mcGrid, &x_prim)) num_of_grids_to_flip[1]+=1; - - x_prim = x; - x_prim[0]=x[0]+1; //1,0 - if(!OutsideDomainCheck(mcGrid, &x_prim)) num_of_grids_to_flip[2]+=1; - - x_prim = x; - x_prim[0]=x[0]+1; - x_prim[1]=x[1]+1; //1,1 - if(!OutsideDomainCheck(mcGrid, &x_prim)) num_of_grids_to_flip[3]+=1; - - }else if(dim==3){ - x_prim = x;//0,0,0 - if(!OutsideDomainCheck(mcGrid, &x_prim)) num_of_grids_to_flip[0]+=1; - - x_prim = x; - x_prim[2]=x[2]+1; //0,0,1 - if(!OutsideDomainCheck(mcGrid, &x_prim)) num_of_grids_to_flip[1]+=1; - - x_prim = x; - x_prim[1]=x[1]+1; //0,1,0 - if(!OutsideDomainCheck(mcGrid, &x_prim)) num_of_grids_to_flip[2]+=1; - - x_prim = x; - x_prim[2]=x[2]+1; - x_prim[1]=x[1]+1; //0,1,1 - if(!OutsideDomainCheck(mcGrid, &x_prim)) num_of_grids_to_flip[3]+=1; - - x_prim = x; - x_prim[0]=x[0]+1; //1,0,0 - if(!OutsideDomainCheck(mcGrid, &x_prim)) num_of_grids_to_flip[4]+=1; - - x_prim = x; - x_prim[2]=x[2]+1; - x_prim[0]=x[0]+1; //1,0,1 - if(!OutsideDomainCheck(mcGrid, &x_prim)) num_of_grids_to_flip[5]+=1; - - x_prim = x; - x_prim[1]=x[1]+1; - x_prim[0]=x[0]+1; //1,1,0 - if(!OutsideDomainCheck(mcGrid, &x_prim)) num_of_grids_to_flip[6]+=1; - - x_prim = x; - x_prim[2]=x[2]+1; - x_prim[1]=x[1]+1; - x_prim[0]=x[0]+1; //1,1,1 - if(!OutsideDomainCheck(mcGrid, &x_prim)) num_of_grids_to_flip[7]+=1; - - } - }// for int j - - for(int k=0; k x (dim,0); - - for (int hh=0; hh=x1(mcGrid, i)){ -// if(x[i]x1(mcGrid, i)-1){ - site_out_of_domain = true; - break;//break from the for int i loop - } - } - if(site_out_of_domain == true){ - hh--; - continue; //continue the int hh loop - } - - double site_selection_probability = 1.0; - - double rd = double(rand())/double(RAND_MAX); - if(rd>site_selection_probability) continue;//this site wont be selected - - int spin1 = (mcGrid)(x); - // determine neighboring spinsss - vector r(dim,0); - std::vector neighbors; - neighbors.clear(); - unsigned int number_of_same_neighours = 0; - if(dim==2){ - for (int i=-1; i<=1; i++) { - for (int j=-1; j<=1; j++) { - if(!(i==0 && j==0)){ - r[0] = x[0] + i; - r[1] = x[1] + j; - if(r[0]=g1(mcGrid, 0) || r[1]=g1(mcGrid, 1) )// not periodic BC - continue;// neighbour outside the global boundary, skip it. - int spin = (mcGrid)(r); - neighbors.push_back(spin); - if(spin==spin1) - number_of_same_neighours++; - } - } - } - }else if(dim==3){ - for (int i=-1; i<=1; i++){ - for (int j=-1; j<=1; j++){ - for (int k=-1; k<=1; k++) { - if(!(i==0 && j==0 && k==0)){ - r[0] = x[0] + i; - r[1] = x[1] + j; - r[2] = x[2] + k; - if(r[0]=g1(mcGrid, 0) || r[1]=g1(mcGrid, 1) || - r[2]=g1(mcGrid, 2))// not periodic BC - continue;// neighbour outside the global boundary, skip it. - int spin = (mcGrid)(r); - neighbors.push_back(spin); - if(spin==spin1) - number_of_same_neighours++; - } - } - } - } - } - - //check if inside a grain - if(number_of_same_neighours==neighbors.size()){//inside a grain - continue;//continue for - } - //choose a random neighbor spin - int spin2 = neighbors[rand()%neighbors.size()]; - // choose a random spin from Q states - if(spin1!=spin2){ - // compute energy change - double dE = 0.0; - if(dim==2){ - for (int i=-1; i<=1; i++){ - for (int j=-1; j<=1; j++){ - if(!(i==0 && j==0)){ - r[0] = x[0] + i; - r[1] = x[1] + j; - if(r[0]=g1(mcGrid, 0) || r[1]=g1(mcGrid, 1) ) // not periodic BC - continue;// neighbour outside the global boundary, skip it. - int spin = (mcGrid)(r); - dE += 1.0/2*((spin!=spin2)-(spin!=spin1)); - }// if(!(i==0 && j==0)) - } - } - } - if(dim==3){ - for (int i=-1; i<=1; i++){ - for (int j=-1; j<=1; j++){ - for (int k=-1; k<=1; k++){ - if(!(i==0 && j==0 && k==0)){ - r[0] = x[0] + i; - r[1] = x[1] + j; - r[2] = x[2] + k; - if(r[0]=g1(mcGrid, 0) || r[1]=g1(mcGrid, 1) || - r[2]=g1(mcGrid, 2)) // not periodic BC - continue;// neighbour outside the global boundary, skip it. - int spin = (mcGrid)(r); - dE += 1.0/2*(spin!=spin2)-(spin!=spin1); - } - } - } - } - } - // attempt a spin flip - double r = double(rand())/double(RAND_MAX); - double kT = 1.3803288e-23*273; - if (dE <= 0.0) (mcGrid)(x) = spin2; - else if (r x (dim, 0); + vector x_prim (dim, 0); + int coordinates_of_cell[dim]; + int initial_coordinates[dim]; + + int num_of_grids_to_flip[( static_cast(pow(2, dim)) )]; + int first_cell_start_coordinates[dim]; + for (int kk = 0; kk < dim; kk++) first_cell_start_coordinates[kk] = x0(mcGrid, kk); + for (int i = 0; i < dim; i++) { + if (x0(mcGrid, i) % 2 != 0) first_cell_start_coordinates[i]--; + } + + + for (int j = 0; j < number_of_lattice_cells; j++) { + int cell_coords_selected[dim]; + if (dim == 2) { + cell_coords_selected[dim - 1] = j % lattice_cells_each_dimension[dim - 1]; //1-indexed + cell_coords_selected[0] = (j / lattice_cells_each_dimension[dim - 1]); + } else if (dim == 3) { + cell_coords_selected[dim - 1] = j % lattice_cells_each_dimension[dim - 1]; //1-indexed + cell_coords_selected[1] = (j / lattice_cells_each_dimension[dim - 1]) % lattice_cells_each_dimension[1]; + cell_coords_selected[0] = ( j / lattice_cells_each_dimension[dim - 1] ) / lattice_cells_each_dimension[1]; + } + for (int i = 0; i < dim; i++) { + x[i] = first_cell_start_coordinates[i] + 2 * cell_coords_selected[i]; + } + + if (dim == 2) { + x_prim = x; + if (!OutsideDomainCheck(mcGrid, &x_prim)) num_of_grids_to_flip[0] += 1; + + x_prim = x; + x_prim[1] = x[1] + 1; //0,1 + if (!OutsideDomainCheck(mcGrid, &x_prim)) num_of_grids_to_flip[1] += 1; + + x_prim = x; + x_prim[0] = x[0] + 1; //1,0 + if (!OutsideDomainCheck(mcGrid, &x_prim)) num_of_grids_to_flip[2] += 1; + + x_prim = x; + x_prim[0] = x[0] + 1; + x_prim[1] = x[1] + 1; //1,1 + if (!OutsideDomainCheck(mcGrid, &x_prim)) num_of_grids_to_flip[3] += 1; + + } else if (dim == 3) { + x_prim = x;//0,0,0 + if (!OutsideDomainCheck(mcGrid, &x_prim)) num_of_grids_to_flip[0] += 1; + + x_prim = x; + x_prim[2] = x[2] + 1; //0,0,1 + if (!OutsideDomainCheck(mcGrid, &x_prim)) num_of_grids_to_flip[1] += 1; + + x_prim = x; + x_prim[1] = x[1] + 1; //0,1,0 + if (!OutsideDomainCheck(mcGrid, &x_prim)) num_of_grids_to_flip[2] += 1; + + x_prim = x; + x_prim[2] = x[2] + 1; + x_prim[1] = x[1] + 1; //0,1,1 + if (!OutsideDomainCheck(mcGrid, &x_prim)) num_of_grids_to_flip[3] += 1; + + x_prim = x; + x_prim[0] = x[0] + 1; //1,0,0 + if (!OutsideDomainCheck(mcGrid, &x_prim)) num_of_grids_to_flip[4] += 1; + + x_prim = x; + x_prim[2] = x[2] + 1; + x_prim[0] = x[0] + 1; //1,0,1 + if (!OutsideDomainCheck(mcGrid, &x_prim)) num_of_grids_to_flip[5] += 1; + + x_prim = x; + x_prim[1] = x[1] + 1; + x_prim[0] = x[0] + 1; //1,1,0 + if (!OutsideDomainCheck(mcGrid, &x_prim)) num_of_grids_to_flip[6] += 1; + + x_prim = x; + x_prim[2] = x[2] + 1; + x_prim[1] = x[1] + 1; + x_prim[0] = x[0] + 1; //1,1,1 + if (!OutsideDomainCheck(mcGrid, &x_prim)) num_of_grids_to_flip[7] += 1; + + } + }// for int j + + for (int k = 0; k < dim; k++) + initial_coordinates[k] = x0(mcGrid, k); + for (int i = 0; i < dim; i++) { + if (x0(mcGrid, i) % 2 != 0) + initial_coordinates[i]--; + } + + for (int step = 0; step < steps; step++) { + if (rank == 0) + print_progress(step, steps); + int num_of_sublattices = 0; + if (dim == 2) num_of_sublattices = 4; + else if (dim == 3) num_of_sublattices = 8; + for (int sublattice = 0; sublattice < num_of_sublattices; sublattice++) { + + srand(time(NULL)); /* seed random number generator */ + vector x (dim, 0); + + for (int hh = 0; hh < num_of_grids_to_flip[sublattice]; hh++) { + int cell_numbering = rand() % (number_of_lattice_cells); //choose a cell to flip, from 0 to num_of_cells_in_thread-1 + int cell_coords_selected[dim]; + if (dim == 2) { + cell_coords_selected[dim - 1] = cell_numbering % lattice_cells_each_dimension[dim - 1]; //1-indexed + cell_coords_selected[0] = (cell_numbering / lattice_cells_each_dimension[dim - 1]); + } else if (dim == 3) { + cell_coords_selected[dim - 1] = cell_numbering % lattice_cells_each_dimension[dim - 1]; //1-indexed + cell_coords_selected[1] = (cell_numbering / lattice_cells_each_dimension[dim - 1]) % lattice_cells_each_dimension[1]; + cell_coords_selected[0] = ( cell_numbering / lattice_cells_each_dimension[dim - 1] ) / lattice_cells_each_dimension[1]; + } + for (int i = 0; i < dim; i++) { + x[i] = first_cell_start_coordinates[i] + 2 * cell_coords_selected[i]; + } + + if (dim == 2) { + switch(sublattice) { + case 0: + break;// 0,0 + case 1: + x[1]++; + break; //0,1 + case 2: + x[0]++; + break; //1,0 + case 3: + x[0]++; + x[1]++; + break; //1,1 + } + } else if (dim == 3) { + switch(sublattice) { + case 0: + break;// 0,0,0 + case 1: + x[2]++; + break; //0,0,1 + case 2: + x[1]++; + break; //0,1,0 + case 3: + x[2]++; + x[1]++; + break; //0,1,1 + case 4: + x[0]++; + break; //1,0,0 + case 5: + x[2]++; + x[0]++; + break; //1,0,1 + case 6: + x[1]++; + x[0]++; + break; //1,1,0 + case 7: + x[2]++; + x[1]++; + x[0]++; //1,1,1 + } + } + + bool site_out_of_domain = false; + for (int i = 0; i < dim; i++) { + if (x[i] < x0(mcGrid, i) || x[i] >= x1(mcGrid, i)) { +// if (x[i]x1(mcGrid, i)-1){ + site_out_of_domain = true; + break;//break from the for int i loop + } + } + if (site_out_of_domain == true) { + hh--; + continue; //continue the int hh loop + } + + double site_selection_probability = 1.0; + + double rd = double(rand()) / double(RAND_MAX); + if (rd > site_selection_probability) continue; //this site wont be selected + + int spin1 = (mcGrid)(x); + // determine neighboring spinsss + vector r(dim, 0); + std::vector neighbors; + neighbors.clear(); + unsigned int number_of_same_neighours = 0; + if (dim == 2) { + for (int i = -1; i <= 1; i++) { + for (int j = -1; j <= 1; j++) { + if (!(i == 0 && j == 0)) { + r[0] = x[0] + i; + r[1] = x[1] + j; + if (r[0] < g0(mcGrid, 0) || r[0] >= g1(mcGrid, 0) || r[1] < g0(mcGrid, 1) || r[1] >= g1(mcGrid, 1) ) // not periodic BC + continue;// neighbour outside the global boundary, skip it. + int spin = (mcGrid)(r); + neighbors.push_back(spin); + if (spin == spin1) + number_of_same_neighours++; + } + } + } + } else if (dim == 3) { + for (int i = -1; i <= 1; i++) { + for (int j = -1; j <= 1; j++) { + for (int k = -1; k <= 1; k++) { + if (!(i == 0 && j == 0 && k == 0)) { + r[0] = x[0] + i; + r[1] = x[1] + j; + r[2] = x[2] + k; + if (r[0] < g0(mcGrid, 0) || r[0] >= g1(mcGrid, 0) || r[1] < g0(mcGrid, 1) || r[1] >= g1(mcGrid, 1) || + r[2] < g0(mcGrid, 2) || r[2] >= g1(mcGrid, 2)) // not periodic BC + continue;// neighbour outside the global boundary, skip it. + int spin = (mcGrid)(r); + neighbors.push_back(spin); + if (spin == spin1) + number_of_same_neighours++; + } + } + } + } + } + + //check if inside a grain + if (number_of_same_neighours == neighbors.size()) { //inside a grain + continue;//continue for + } + //choose a random neighbor spin + int spin2 = neighbors[rand() % neighbors.size()]; + // choose a random spin from Q states + if (spin1 != spin2) { + // compute energy change + double dE = 0.0; + if (dim == 2) { + for (int i = -1; i <= 1; i++) { + for (int j = -1; j <= 1; j++) { + if (!(i == 0 && j == 0)) { + r[0] = x[0] + i; + r[1] = x[1] + j; + if (r[0] < g0(mcGrid, 0) || r[0] >= g1(mcGrid, 0) || r[1] < g0(mcGrid, 1) || r[1] >= g1(mcGrid, 1) ) // not periodic BC + continue;// neighbour outside the global boundary, skip it. + int spin = (mcGrid)(r); + dE += 1.0 / 2 * ((spin != spin2) - (spin != spin1)); + }// if (!(i==0 && j==0)) + } + } + } + if (dim == 3) { + for (int i = -1; i <= 1; i++) { + for (int j = -1; j <= 1; j++) { + for (int k = -1; k <= 1; k++) { + if (!(i == 0 && j == 0 && k == 0)) { + r[0] = x[0] + i; + r[1] = x[1] + j; + r[2] = x[2] + k; + if (r[0] < g0(mcGrid, 0) || r[0] >= g1(mcGrid, 0) || r[1] < g0(mcGrid, 1) || r[1] >= g1(mcGrid, 1) || + r[2] < g0(mcGrid, 2) || r[2] >= g1(mcGrid, 2)) // not periodic BC + continue;// neighbour outside the global boundary, skip it. + int spin = (mcGrid)(r); + dE += 1.0 / 2 * (spin != spin2) - (spin != spin1); + } + } + } + } + } + // attempt a spin flip + double r = double(rand()) / double(RAND_MAX); + double kT = 1.3803288e-23 * 273; + if (dE <= 0.0) (mcGrid)(x) = spin2; + else if (r < exp(-dE / kT)) (mcGrid)(x) = spin2; + }//spin1!=spin2 + } // hh + +#ifdef MPI_VERSION MPI::COMM_WORLD.Barrier(); - #endif - ghostswap(mcGrid,sublattice); // once looped over a "color", ghostswap. - #ifdef MPI_VERSION +#endif + ghostswap(mcGrid, sublattice); // once looped over a "color", ghostswap. +#ifdef MPI_VERSION MPI::COMM_WORLD.Barrier(); - #endif +#endif }//loop over sublattice }//loop over step }//update From 0de3eefea4f1fbe13bd5de8e5cf6c38cc1e3bc05 Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Tue, 2 Feb 2016 15:13:36 -0500 Subject: [PATCH 12/83] remove redundant progress_bar declarations --- algorithms/evolvers.h | 2 -- algorithms/generators.cpp | 25 +------------------------ algorithms/generators.h | 2 -- algorithms/tessellate.hpp | 23 ----------------------- 4 files changed, 1 insertion(+), 51 deletions(-) diff --git a/algorithms/evolvers.h b/algorithms/evolvers.h index 9f1e982..f90495d 100644 --- a/algorithms/evolvers.h +++ b/algorithms/evolvers.h @@ -2,8 +2,6 @@ // Definitions of methods to evolve MMSP grids #ifndef _EVOLVERS_H_ #define _EVOLVERS_H_ -void print_progress(const int step, const int steps, const int iterations); - template void interface_field_evolves(const T& dt, const T& width, const T& gamma, const T& epsilon, const T& w, const T& mu, const MMSP::grid >& former, MMSP::grid >& latter, const int node); diff --git a/algorithms/generators.cpp b/algorithms/generators.cpp index ef369c9..e37db0f 100644 --- a/algorithms/generators.cpp +++ b/algorithms/generators.cpp @@ -90,8 +90,8 @@ void exact_voronoi(MMSP::grid >& grid, const std::vector x=position(grid,n); int min_distance=std::numeric_limits::max(); int identity=-1, min_identity=identity; @@ -115,7 +115,6 @@ void exact_voronoi(MMSP::grid >& grid, const std::vector >& grid, const int& nseeds) { #endif } // tessellate -void print_progress(const int i, const int N) { - char* timestring; - static unsigned long tstart; - struct tm* timeinfo; - - if (i==0) { - tstart = time(NULL); - std::time_t rawtime; - std::time( &rawtime ); - timeinfo = std::localtime( &rawtime ); - timestring = std::asctime(timeinfo); - timestring[std::strlen(timestring)-1] = '\0'; - std::cout< void honeycomb_seeds(const int x0[dim], const int x1[dim], const int g0[dim], const int g1[dim], const int a, std::vector > >& seeds); -void print_progress(const int i, const int N); - #endif // _GENERATORS_H_ diff --git a/algorithms/tessellate.hpp b/algorithms/tessellate.hpp index 3373498..f59cbab 100644 --- a/algorithms/tessellate.hpp +++ b/algorithms/tessellate.hpp @@ -16,8 +16,6 @@ #include #include "MersenneTwister.h" -void print_progress(const int i, const int N); - template double radius(const MMSP::vector& a, const MMSP::vector& b) { double radius=0.0; for (int d=0; d >& grid, const std::string& seed_filen } // namespace -void print_progress(const int i, const int N) { - char* timestring; - static unsigned long tstart; - struct tm* timeinfo; - - if (i==0) { - tstart = time(NULL); - std::time_t rawtime; - std::time( &rawtime ); - timeinfo = std::localtime( &rawtime ); - timestring = std::asctime(timeinfo); - timestring[std::strlen(timestring)-1] = '\0'; - std::cout< Date: Tue, 2 Feb 2016 15:58:55 -0500 Subject: [PATCH 13/83] adapt to common naming scheme --- .../phase_transitions/solidification/anisotropic/Makefile | 8 ++++---- .../anisotropic/{dendritic.cpp => solidification.cpp} | 8 ++++---- .../anisotropic/{dendritic.hpp => solidification.hpp} | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) rename examples/phase_transitions/solidification/anisotropic/{dendritic.cpp => solidification.cpp} (98%) rename examples/phase_transitions/solidification/anisotropic/{dendritic.hpp => solidification.hpp} (84%) diff --git a/examples/phase_transitions/solidification/anisotropic/Makefile b/examples/phase_transitions/solidification/anisotropic/Makefile index c66436b..6e6ba5d 100644 --- a/examples/phase_transitions/solidification/anisotropic/Makefile +++ b/examples/phase_transitions/solidification/anisotropic/Makefile @@ -19,14 +19,14 @@ core = $(incdir)/MMSP.main.hpp \ $(incdir)/MMSP.sparse.hpp # the program -dendritic.out: dendritic.cpp $(core) +solidification.out: solidification.cpp $(core) $(compiler) $(flags) $< -o $@ -lz mmsp2png: mmsp2png.cpp $(core) /usr/include/IL/devil_cpp_wrapper.hpp $(compiler) $(flags) -I /usr/include/IL -include il.h $< -o $@ -lz -lIL -lILU -lILUT -parallel: dendritic.cpp $(core) - $(pcompiler) $(pflags) $< -o $@_dendritic.out -lz +parallel: solidification.cpp $(core) + $(pcompiler) $(pflags) $< -o $@_solidification.out -lz clean: - rm -rf dendritic.out parallel_dendritic.out mmsp2png + rm -rf solidification.out parallel_solidification.out mmsp2png diff --git a/examples/phase_transitions/solidification/anisotropic/dendritic.cpp b/examples/phase_transitions/solidification/anisotropic/solidification.cpp similarity index 98% rename from examples/phase_transitions/solidification/anisotropic/dendritic.cpp rename to examples/phase_transitions/solidification/anisotropic/solidification.cpp index c0a386d..9a4e2f6 100644 --- a/examples/phase_transitions/solidification/anisotropic/dendritic.cpp +++ b/examples/phase_transitions/solidification/anisotropic/solidification.cpp @@ -1,14 +1,14 @@ -// dendritic.cpp +// solidification.cpp // Anisotropic solidification by 2D phase field method // Questions/comments to kellet@rpi.edu (Trevor Keller) -#ifndef DENDRITIC_UPDATE -#define DENDRITIC_UPDATE +#ifndef SOLIDIFICATION_UPDATE +#define SOLIDIFICATION_UPDATE #include #include #include #include"MMSP.hpp" -#include"dendritic.hpp" +#include"solidification.hpp" namespace MMSP{ diff --git a/examples/phase_transitions/solidification/anisotropic/dendritic.hpp b/examples/phase_transitions/solidification/anisotropic/solidification.hpp similarity index 84% rename from examples/phase_transitions/solidification/anisotropic/dendritic.hpp rename to examples/phase_transitions/solidification/anisotropic/solidification.hpp index 64a6764..7a94977 100644 --- a/examples/phase_transitions/solidification/anisotropic/dendritic.hpp +++ b/examples/phase_transitions/solidification/anisotropic/solidification.hpp @@ -1,8 +1,8 @@ -// dendritic.hpp +// solidification.hpp // Anisotropic dendritic solidification code // Questions/comments to kellet@rpi.edu (Trevor Keller) -std::string PROGRAM = "dendritic"; +std::string PROGRAM = "solidification"; std::string MESSAGE = "Anisotropic dendritic solidification code"; typedef MMSP::grid<1,MMSP::vector > GRID1D; From d361289817af2b0dc15d8d3ffa637bdbed9779b1 Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Tue, 2 Feb 2016 18:27:48 -0500 Subject: [PATCH 14/83] build examples as a simple sanity check More elegant and continuous-integration-compatible approaches exist. This is a quick & dirty spot check of whether the examples compile. --- examples/beginners_diffusion/Makefile | 2 +- test/build_examples.sh | 226 ++++++++++++++++++++++++++ 2 files changed, 227 insertions(+), 1 deletion(-) create mode 100755 test/build_examples.sh diff --git a/examples/beginners_diffusion/Makefile b/examples/beginners_diffusion/Makefile index 4dc7d05..fbf70b0 100644 --- a/examples/beginners_diffusion/Makefile +++ b/examples/beginners_diffusion/Makefile @@ -48,5 +48,5 @@ gnuplot_i.hpp.gch : gnuplot_i.hpp clean : - rm *.o *~ *.gch + rm -rf go *.o *~ *.gch diff --git a/test/build_examples.sh b/test/build_examples.sh new file mode 100755 index 0000000..37dbb68 --- /dev/null +++ b/test/build_examples.sh @@ -0,0 +1,226 @@ +#!/bin/bash + +cd ../examples + +while [[ $# > 0 ]] +do + key="$1" + case $key in + -c|--clean) + CLEAN=true + shift # past argument + ;; + *) + # unknown option + echo "WARNING: Unknown option ${key}." + echo + ;; + esac + shift # past argument or value +done + +if [[ $CLEAN ]]; then + echo "Building examples in serial and parallel, then deleting binaries." +else + echo "Building examples in serial and parallel, leaving binaries behind (specify --clean to clean up)." +fi +echo + +examples=$(pwd) + +cd beginners_diffusion/ +pwd + make -Bs || exit $? + if $CLEAN; then make -s clean; fi + cd $examples + +echo +cd coarsening +pwd +cd grain_growth +pwd +cd anisotropic +pwd + cd Monte_Carlo + pwd + make -Bs || exit $? + make -Bs parallel || exit $? + if $CLEAN; then make -s clean; fi + cd ../phase_field/ + pwd + make -Bs || exit $? + make -Bs parallel || exit $? + if $CLEAN; then make -s clean; fi + cd ../sparsePF/ + pwd + make -Bs || exit $? + make -Bs parallel || exit $? + if $CLEAN; then make -s clean; fi + cd $examples + +cd coarsening/grain_growth/isotropic +pwd + cd Monte_Carlo/ + pwd + make -Bs || exit $? + make -Bs parallel || exit $? + if $CLEAN; then make -s clean; fi + cd ../phase_field/ + pwd + make -Bs || exit $? + make -Bs parallel || exit $? + if $CLEAN; then make -s clean; fi + cd ../sparsePF/ + pwd + make -Bs || exit $? + make -Bs parallel || exit $? + if $CLEAN; then make -s clean; fi + cd $examples + +echo +cd coarsening/ostwald_ripening +pwd +cd isotropic +pwd + cd phase_field/ + pwd + make -Bs || exit $? + make -Bs parallel || exit $? + if $CLEAN; then make -s clean; fi + cd $examples + +echo +cd coarsening/zener_pinning +pwd +cd anisotropic +pwd + cd Monte_Carlo/ + pwd + make -Bs || exit $? + make -Bs parallel || exit $? + if $CLEAN; then make -s clean; fi + cd ../phase_field/ + pwd + make -Bs || exit $? + make -Bs parallel || exit $? + if $CLEAN; then make -s clean; fi + cd ../sparsePF/ + pwd + make -Bs || exit $? + make -Bs parallel || exit $? + if $CLEAN; then make -s clean; fi + cd $examples + +cd coarsening/zener_pinning/isotropic +pwd + cd Monte_Carlo/ + pwd + make -Bs || exit $? + make -Bs parallel || exit $? + if $CLEAN; then make -s clean; fi + cd ../phase_field/ + pwd + make -Bs || exit $? + make -Bs parallel || exit $? + if $CLEAN; then make -s clean; fi + cd ../sparsePF/ + pwd + make -Bs || exit $? + make -Bs parallel || exit $? + if $CLEAN; then make -s clean; fi + cd $examples + +echo +cd differential_equations +pwd +cd elliptic +pwd + cd Poisson/ + pwd + make -Bs || exit $? + make -Bs parallel || exit $? + if $CLEAN; then make -s clean; fi + cd $examples + +echo +cd phase_transitions +pwd + cd allen-cahn/ + pwd + make -Bs || exit $? + make -Bs parallel || exit $? + if $CLEAN; then make -s clean; fi + cd $examples + + cd phase_transitions/cahn-hilliard + pwd + cd convex_splitting/ + pwd + make -Bs || exit $? + make -Bs parallel || exit $? + if $CLEAN; then make -s clean; fi + cd ../explicit/ + pwd + make -Bs || exit $? + make -Bs parallel || exit $? + if $CLEAN; then make -s clean; fi + cd $examples + + cd phase_transitions/model_A + pwd + make -Bs || exit $? + make -Bs parallel || exit $? + if $CLEAN; then make -s clean; fi + cd $examples + + cd phase_transitions/model_B/ + pwd + make -Bs || exit $? + make -Bs parallel || exit $? + if $CLEAN; then make -s clean; fi + cd $examples + + cd phase_transitions/spinodal/ + pwd + make -Bs || exit $? + make -Bs parallel || exit $? + if $CLEAN; then make -s clean; fi + cd $examples + +cd phase_transitions/solidification +pwd + cd anisotropic/ + pwd + make -Bs || exit $? + make -Bs parallel || exit $? + if $CLEAN; then make -s clean; fi + cd $examples + +echo +cd statistical_mechanics +pwd + cd Heisenberg/ + pwd + make -Bs || exit $? + make -Bs parallel || exit $? + if $CLEAN; then make -s clean; fi + cd $examples + + cd statistical_mechanics/Ising/ + pwd + make -Bs || exit $? + make -Bs parallel || exit $? + if $CLEAN; then make -s clean; fi + cd $examples + + cd statistical_mechanics/Potts/ + pwd + make -Bs || exit $? + make -Bs parallel || exit $? + if $CLEAN; then make -s clean; fi + cd $examples + +echo +echo "Examples built successfully." + +cd ../test/ From 9d6f3020bbb7efffe25d302c0cfd90dbe0c7f37a Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Wed, 3 Feb 2016 20:56:22 -0500 Subject: [PATCH 15/83] migrate from CxxTest to GTest Google's GTest framework provides better support for templated classes and "death tests", which would take a lot of code or crash CxxTest. GTest is included as a submodule, and will not be included in naive clones. This requires documentation! --- .gitmodules | 3 + test/MMSP.unitTestSuite.cpp | 173 +++++++++++++++++++++++ test/MMSP.unitTestSuite.hpp | 274 ------------------------------------ test/Makefile | 35 ++++- test/gtest | 1 + 5 files changed, 206 insertions(+), 280 deletions(-) create mode 100644 .gitmodules create mode 100644 test/MMSP.unitTestSuite.cpp delete mode 100644 test/MMSP.unitTestSuite.hpp create mode 160000 test/gtest diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..0a27a17 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "test/gtest"] + path = test/gtest + url = git@github.com:google/googletest.git diff --git a/test/MMSP.unitTestSuite.cpp b/test/MMSP.unitTestSuite.cpp new file mode 100644 index 0000000..fcac71b --- /dev/null +++ b/test/MMSP.unitTestSuite.cpp @@ -0,0 +1,173 @@ +/* MMSP.unitTest.hpp +** Use Google Test to build unit tests for MMSP classes +** http://github.com/google/googletest +*/ +#include"MMSP.hpp" +#include + +using testing::Types; +typedef Types datatypes; + + + +template +class scalarTest : public testing::Test +{ +protected: + virtual void SetUp() { + value = 2; + } + + MMSP::scalar value; +}; + +TYPED_TEST_CASE(scalarTest, datatypes); + +TYPED_TEST(scalarTest, testSize) { + EXPECT_EQ(this->value.buffer_size(),sizeof(TypeParam)); +} +TYPED_TEST(scalarTest, testAddition) { + EXPECT_EQ(this->value+this->value,static_cast(4)); +} +TYPED_TEST(scalarTest, testSquares) { + EXPECT_EQ(this->value*this->value,static_cast(4)); +} +TYPED_TEST(scalarTest, testMultiplication) { + EXPECT_EQ(2*this->value,static_cast(4)); + EXPECT_EQ(this->value*2,static_cast(4)); +} + + + + +template +class vectorTest : public testing::Test +{ +protected: + virtual void SetUp() { + MMSP::vector temp(3,2); + value = temp; + } + + MMSP::vector value; +}; + +TYPED_TEST_CASE(vectorTest, datatypes); + +TYPED_TEST(vectorTest, testValue) { + for (int i=0; i<3; i++) { + EXPECT_EQ(this->value[i],static_cast(2)); + } +} +TYPED_TEST(vectorTest, testSize) { + EXPECT_EQ(this->value.length(),3); + EXPECT_EQ(this->value.buffer_size(),sizeof(TypeParam)); + EXPECT_DEATH(this->value[4],"index exceeds vector size"); +} +TYPED_TEST(vectorTest, testAddition) { + MMSP::vector temp = this->value+this->value; + for (int i=0; i<3; i++) + EXPECT_EQ(temp[i],static_cast(4)); +} +TYPED_TEST(vectorTest, testIncrement) { + this->value+=this->value; + for (int i=0; i<3; i++) + EXPECT_EQ(this->value[i],static_cast(4)); +} +TYPED_TEST(vectorTest, testSquares) { + EXPECT_EQ(this->value*this->value,12); +} +TYPED_TEST(vectorTest, testMultiplication) { + MMSP::vector a = 2*this->value; + //MMSP::vector b = this->value*2; + for (int i=0; i<3; i++) { + EXPECT_EQ(a[i],static_cast(4)); + //EXPECT_EQ(b[i],static_cast(4)); // no such operator + } +} + + + + +template +class sparseTest : public testing::Test +{ +protected: + virtual void SetUp() { + for (int i=0; i<3; i++) + value.set(2*i) = 2; + } + + MMSP::sparse value; +}; + + +TYPED_TEST_CASE(sparseTest, datatypes); + +TYPED_TEST(sparseTest, testValue) { + for (int i=0; i<6; i+=2) + EXPECT_EQ(this->value[i],static_cast(2)); + for (int i=1; i<7; i+=2) + EXPECT_EQ(this->value[i],0); +} +TYPED_TEST(sparseTest, testSize) { + EXPECT_EQ(this->value.length(),3); + unsigned int delta = std::abs(sizeof(int) - sizeof(TypeParam)); + EXPECT_EQ(this->value.buffer_size(),sizeof(int) + 2*3*sizeof(TypeParam) + 3*delta); +} +TYPED_TEST(sparseTest, testAddition) { + MMSP::sparse temp = this->value+this->value; + for (int i=0; i<3; i++) + EXPECT_EQ(temp.value(i),static_cast(4)); +} +TYPED_TEST(sparseTest, testIncrement) { + this->value+=this->value; + for (int i=0; i<3; i++) + EXPECT_EQ(this->value.value(i),static_cast(4)); +} +TYPED_TEST(sparseTest, testMultiplication) { + MMSP::sparse a = 2*this->value; + //MMSP::sparse b = this->value*2; + for (int i=0; i<3; i++) { + EXPECT_EQ(a.value(i),static_cast(4)); + //EXPECT_EQ(b.value(i),static_cast(4)); // no such operator + } +} + + + + + +template +class gridTest : public testing::Test +{ +protected: + // MUST call grid constructor in test constructor + gridTest() : grid1D(1, 0, 64) { + for (int n=0; n > grid1D; +}; + +TYPED_TEST_CASE(gridTest, datatypes); + +TYPED_TEST(gridTest, testSize) { + #ifndef MPI_VERSION + EXPECT_EQ(MMSP::nodes(this->grid1D),64); + #else + int np = MPI::COMM_WORLD.Get_size(); + EXPECT_LE(MMSP::nodes(this->grid1D),64/np); + #endif + EXPECT_EQ(this->grid1D.buffer_size(),MMSP::nodes(this->grid1D)*sizeof(TypeParam)); +} + + diff --git a/test/MMSP.unitTestSuite.hpp b/test/MMSP.unitTestSuite.hpp deleted file mode 100644 index 24e4352..0000000 --- a/test/MMSP.unitTestSuite.hpp +++ /dev/null @@ -1,274 +0,0 @@ -/* MMSP.unitTestSuite.hpp -** Use CxxTest to build unit tests for MMSP classes -** http://cxxtest.com -*/ -#include"MMSP.hpp" -#include - -class scalarTestSuite : public CxxTest::TestSuite -{ -private: - MMSP::scalar sc; - MMSP::scalar ss; - MMSP::scalar si; - MMSP::scalar sl; - MMSP::scalar sf; - MMSP::scalar sd; - -public: - void setUp() { - sc = 2; - ss = 2; - si = 2; - sl = 2; - sf = 2.0; - sd = 2.0; - } - void testSize(void) { - TS_ASSERT_EQUALS(sc.buffer_size(),sizeof(char)); - TS_ASSERT_EQUALS(ss.buffer_size(),sizeof(short)); - TS_ASSERT_EQUALS(si.buffer_size(),sizeof(int)); - TS_ASSERT_EQUALS(sl.buffer_size(),sizeof(long)); - TS_ASSERT_EQUALS(sf.buffer_size(),sizeof(float)); - TS_ASSERT_EQUALS(sd.buffer_size(),sizeof(double)); - } - void testAddition(void) { - TS_ASSERT_EQUALS(sc+sc,char(4)); - TS_ASSERT_EQUALS(ss+ss,short(4)); - TS_ASSERT_EQUALS(si+si,int(4)); - TS_ASSERT_EQUALS(sl+sl,long(4)); - TS_ASSERT_EQUALS(sf+sf,float(4)); - TS_ASSERT_EQUALS(sd+sd,double(4)); - } - void testSquares(void) { - TS_ASSERT_EQUALS(sc*sc,char(4)); - TS_ASSERT_EQUALS(ss*ss,short(4)); - TS_ASSERT_EQUALS(si*si,int(4)); - TS_ASSERT_EQUALS(sl*sl,long(4)); - TS_ASSERT_EQUALS(sf*sf,float(4)); - TS_ASSERT_EQUALS(sd*sd,double(4)); - } - void testMultiplication(void) { - TS_ASSERT_EQUALS(2*sc,char(4)); - TS_ASSERT_EQUALS(2*ss,short(4)); - TS_ASSERT_EQUALS(2*si,int(4)); - TS_ASSERT_EQUALS(2*sl,long(4)); - TS_ASSERT_EQUALS(2*sf,float(4)); - TS_ASSERT_EQUALS(2*sd,double(4)); - } -}; - -class vectorTestSuite : public CxxTest::TestSuite -{ -private: - MMSP::vector vc; - MMSP::vector vs; - MMSP::vector vi; - MMSP::vector vl; - MMSP::vector vf; - MMSP::vector vd; - -public: - void setUp() { - MMSP::vector temp(3,2); - vc = temp; - vs = temp; - vi = temp; - vl = temp; - vf = temp; - vd = temp; - } - void testValue(void) { - for (int i=0; i<3; i++) { - TS_ASSERT_EQUALS(vc[i],char(2)); - TS_ASSERT_EQUALS(vs[i],short(2)); - TS_ASSERT_EQUALS(vi[i],int(2)); - TS_ASSERT_EQUALS(vl[i],long(2)); - TS_ASSERT_EQUALS(vf[i],float(2)); - TS_ASSERT_EQUALS(vd[i],double(2)); - } - } - void testSize(void) { - TS_ASSERT_EQUALS(vc.length(),3); - TS_ASSERT_EQUALS(vs.length(),3); - TS_ASSERT_EQUALS(vi.length(),3); - TS_ASSERT_EQUALS(vl.length(),3); - TS_ASSERT_EQUALS(vf.length(),3); - TS_ASSERT_EQUALS(vd.length(),3); - - TS_ASSERT_EQUALS(vc.buffer_size(),sizeof(int)+3*sizeof(char)); - TS_ASSERT_EQUALS(vs.buffer_size(),sizeof(int)+3*sizeof(short)); - TS_ASSERT_EQUALS(vi.buffer_size(),4*sizeof(int)); - TS_ASSERT_EQUALS(vl.buffer_size(),sizeof(int)+3*sizeof(long)); - TS_ASSERT_EQUALS(vf.buffer_size(),sizeof(int)+3*sizeof(float)); - TS_ASSERT_EQUALS(vd.buffer_size(),sizeof(int)+3*sizeof(double)); - - TS_ASSERT_THROWS_ANYTHING(vc[4]); - TS_ASSERT_THROWS_ANYTHING(vs[4]); - TS_ASSERT_THROWS_ANYTHING(vi[4]); - TS_ASSERT_THROWS_ANYTHING(vl[4]); - TS_ASSERT_THROWS_ANYTHING(vf[4]); - TS_ASSERT_THROWS_ANYTHING(vd[4]); - } - void testAddition(void) { - MMSP::vector temp(3,4); - for (int i=0; i<3; i++) { - TS_ASSERT_EQUALS((vc+vc)[i],temp[i]); - TS_ASSERT_EQUALS((vs+vs)[i],temp[i]); - TS_ASSERT_EQUALS((vi+vi)[i],temp[i]); - TS_ASSERT_EQUALS((vl+vl)[i],temp[i]); - TS_ASSERT_EQUALS((vf+vf)[i],temp[i]); - TS_ASSERT_EQUALS((vd+vd)[i],temp[i]); - } - } - void testSquares(void) { - // v*v is the inner (dot) product - MMSP::vector temp(3,4); - TS_ASSERT_EQUALS(vc*vc,12); - TS_ASSERT_EQUALS(vs*vs,12); - TS_ASSERT_EQUALS(vi*vi,12); - TS_ASSERT_EQUALS(vl*vl,12); - TS_ASSERT_EQUALS(vf*vf,12); - TS_ASSERT_EQUALS(vd*vd,12); - } - void testMultiplication(void) { - MMSP::vector temp(3,4); - for (int i=0; i<3; i++) { - TS_ASSERT_EQUALS((2*vc)[i],temp[i]); - TS_ASSERT_EQUALS((2*vi)[i],temp[i]); - TS_ASSERT_EQUALS((2*vl)[i],temp[i]); - TS_ASSERT_EQUALS((2*vf)[i],temp[i]); - TS_ASSERT_EQUALS((2*vd)[i],temp[i]); - } - } -}; - -class sparseTestSuite : public CxxTest::TestSuite -{ -private: - MMSP::sparse svc; - MMSP::sparse svs; - MMSP::sparse svi; - MMSP::sparse svl; - MMSP::sparse svf; - MMSP::sparse svd; - template void sparseInit(MMSP::sparse& s) { - MMSP::sparse temp; - for (int i=0; i<3; i++) - temp.set(2*i) = 2; - s = temp; - } - -public: - void setUp() { - sparseInit(svc); - sparseInit(svs); - sparseInit(svi); - sparseInit(svl); - sparseInit(svf); - sparseInit(svd); - } - void testValue(void) { - for (int i=0; i<6; i+=2) { - TS_ASSERT_EQUALS(svc[i],char(2)); - TS_ASSERT_EQUALS(svs[i],short(2)); - TS_ASSERT_EQUALS(svi[i],int(2)); - TS_ASSERT_EQUALS(svl[i],long(2)); - TS_ASSERT_EQUALS(svf[i],float(2)); - TS_ASSERT_EQUALS(svd[i],double(2)); - } - for (int i=1; i<5; i+=2) { - TS_ASSERT_EQUALS(svc[i],char(0)); - TS_ASSERT_EQUALS(svs[i],short(0)); - TS_ASSERT_EQUALS(svi[i],int(0)); - TS_ASSERT_EQUALS(svl[i],long(0)); - TS_ASSERT_EQUALS(svf[i],float(0)); - TS_ASSERT_EQUALS(svd[i],double(0)); - } - } - void testSize(void) { - TS_ASSERT_EQUALS(svc.length(),3); - TS_ASSERT_EQUALS(svs.length(),3); - TS_ASSERT_EQUALS(svi.length(),3); - TS_ASSERT_EQUALS(svl.length(),3); - TS_ASSERT_EQUALS(svf.length(),3); - TS_ASSERT_EQUALS(svd.length(),3); - - TS_ASSERT_DIFFERS(sizeof(int)+sizeof(char),sizeof(MMSP::item)); // that is unexpected - TS_ASSERT_EQUALS(sizeof(int)+sizeof(int),sizeof(MMSP::item)); // that is unexpected - TS_ASSERT_EQUALS(svc.buffer_size(),sizeof(int)+3*sizeof(int)+3*sizeof(char)+3*(sizeof(int)-sizeof(char))); - TS_ASSERT_EQUALS(svs.buffer_size(),sizeof(int)+3*sizeof(int)+3*sizeof(short)+3*(sizeof(int)-sizeof(short))); - TS_ASSERT_EQUALS(svi.buffer_size(),sizeof(int)+3*sizeof(int)+3*sizeof(int)); - TS_ASSERT_EQUALS(svl.buffer_size(),sizeof(int)+3*sizeof(int)+3*sizeof(long)+3*(sizeof(long)-sizeof(int))); - TS_ASSERT_EQUALS(svf.buffer_size(),sizeof(int)+3*sizeof(int)+3*sizeof(float)); - TS_ASSERT_EQUALS(svd.buffer_size(),sizeof(int)+3*sizeof(int)+3*sizeof(double)+3*(sizeof(double)-sizeof(int))); - // More compactly: - TS_ASSERT_EQUALS(svc.buffer_size(),sizeof(int)+3*sizeof(int)+3*sizeof(int)); - TS_ASSERT_EQUALS(svs.buffer_size(),sizeof(int)+3*sizeof(int)+3*sizeof(int)); - TS_ASSERT_EQUALS(svi.buffer_size(),sizeof(int)+3*sizeof(int)+3*sizeof(int)); - TS_ASSERT_EQUALS(svl.buffer_size(),sizeof(int)+3*sizeof(long)+3*sizeof(long)); - TS_ASSERT_EQUALS(svf.buffer_size(),sizeof(int)+3*sizeof(int)+3*sizeof(float)); - TS_ASSERT_EQUALS(svd.buffer_size(),sizeof(int)+3*sizeof(double)+3*sizeof(double)); - } - void testAddition(void) { - MMSP::sparse temp; - for (int i=0; i<3; i++) - temp.set(2*i) = 4; - TS_ASSERT_EQUALS(svi+svi,temp); - } - // Note: Multiplying two sparse vectors is undefined by design. - void testMultiplication(void) { - TS_ASSERT_EQUALS((2*svc)[0],4); - TS_ASSERT_EQUALS((2*svs)[0],4); - TS_ASSERT_EQUALS((2*svi)[0],4); - TS_ASSERT_EQUALS((2*svl)[0],4); - TS_ASSERT_EQUALS((2*svf)[0],4); - TS_ASSERT_EQUALS((2*svd)[0],4); - } -}; - -class gridTestSuite : public CxxTest::TestSuite -{ -private: - -public: - void setUp(int argc, char* argv[]) { - MMSP::Init(argc, argv); - } - void tearDown() { - MMSP::Finalize(); - } - void testSize1D() { - MMSP::grid<1,MMSP::scalar > g1c(0, 0, 64); - MMSP::grid<1,MMSP::scalar > g1s(0, 0, 64); - MMSP::grid<1,MMSP::scalar > g1i(0, 0, 64); - MMSP::grid<1,MMSP::scalar > g1l(0, 0, 64); - MMSP::grid<1,MMSP::scalar > g1f(0, 0, 64); - MMSP::grid<1,MMSP::scalar > g1d(0, 0, 64); - - #ifndef MPI_VERSION - TS_ASSERT_EQUALS(MMSP::nodes(g1c),64); - TS_ASSERT_EQUALS(MMSP::nodes(g1s),64); - TS_ASSERT_EQUALS(MMSP::nodes(g1i),64); - TS_ASSERT_EQUALS(MMSP::nodes(g1l),64); - TS_ASSERT_EQUALS(MMSP::nodes(g1f),64); - TS_ASSERT_EQUALS(MMSP::nodes(g1d),64); - #else - int np = MPI::COMM_WORLD.Get_size(); - TS_ASSERT_LESS_THAN_EQUALS(MMSP::nodes(g1c),64/np); - TS_ASSERT_LESS_THAN_EQUALS(MMSP::nodes(g1s),64/np); - TS_ASSERT_LESS_THAN_EQUALS(MMSP::nodes(g1i),64/np); - TS_ASSERT_LESS_THAN_EQUALS(MMSP::nodes(g1l),64/np); - TS_ASSERT_LESS_THAN_EQUALS(MMSP::nodes(g1f),64/np); - TS_ASSERT_LESS_THAN_EQUALS(MMSP::nodes(g1d),64/np); - #endif - - TS_ASSERT_EQUALS(g1c.buffer_size(),MMSP::nodes(g1c)*sizeof(char)); - TS_ASSERT_EQUALS(g1s.buffer_size(),MMSP::nodes(g1s)*sizeof(short)); - TS_ASSERT_EQUALS(g1i.buffer_size(),MMSP::nodes(g1i)*sizeof(int)); - TS_ASSERT_EQUALS(g1l.buffer_size(),MMSP::nodes(g1l)*sizeof(long)); - TS_ASSERT_EQUALS(g1f.buffer_size(),MMSP::nodes(g1f)*sizeof(float)); - TS_ASSERT_EQUALS(g1d.buffer_size(),MMSP::nodes(g1d)*sizeof(double)); - - } -}; diff --git a/test/Makefile b/test/Makefile index fc751f4..c572de7 100644 --- a/test/Makefile +++ b/test/Makefile @@ -1,15 +1,18 @@ # Makefile # GNU makefile for single and parallel test programs using MMSP +# and the Google unit test framework (gtest) # Questions/comments to gruberja@gmail.com (Jason Gruber) # includes incdir = ../include +gtdir = gtest/googletest # compilers/flags compiler = g++ -flags = -O0 -I $(incdir) +flags = -g -I$(incdir) +gflags = -isystem $(gtdir)/include -I$(gtdir) $(flags) -I$(gtdir)/include -pthread pcompiler = mpic++ -pflags = -O0 -I $(incdir) -include mpi.h +pflags = $(flags) -include mpi.h # dependencies core = $(incdir)/MMSP.hpp \ @@ -26,10 +29,30 @@ test: test.cpp $(core) parallel: test.cpp $(core) $(pcompiler) $(pflags) $< -o $@ -unitTestSuite: MMSP.unitTestSuite.hpp - cxxtestgen --error-printer -o MMSP.unitTestSuite.cpp $< && \ - $(compiler) $(flags) -o $@ MMSP.unitTestSuite.cpp $(core) && \ +# GTEST + + +gthdr = $(gtdir)/include/gtest/*.h \ + $(gtdir)/include/gtest/internal/*.h + +gtsrc = $(gtdir)/src/*.cc \ + $(gtdir)/src/*.h + +gtest-all.o: $(gtsrc) $(gthdr) + $(compiler) $(gflags) -c $(gtdir)/src/gtest-all.cc + +gtest_main.o: $(gtsrc) $(gthdr) + $(compiler) $(gflags) -c $(gtdir)/src/gtest_main.cc + +gtest_main.a: gtest-all.o gtest_main.o + $(AR) $(ARFLAGS) $@ $^ + +MMSP.unitTestSuite.o: $(core) $(gthdr) + $(compiler) $(gflags) -c MMSP.unitTestSuite.cpp + +unitTestSuite: MMSP.unitTestSuite.o gtest_main.a + $(compiler) $(gflags) $^ -o $@ -lpthread -lz && \ ./$@ clean: - rm -f test parallel MMSP.unitTestSuite.cpp unitTestSuite + rm -f test parallel unitTestSuite *.a *.o diff --git a/test/gtest b/test/gtest new file mode 160000 index 0000000..ff5ffd4 --- /dev/null +++ b/test/gtest @@ -0,0 +1 @@ +Subproject commit ff5ffd457e032c8be8a64a7a94c824063c8b11e3 From 9a30e6e9e1b78a59a7360b5e91539d2dd0b906a0 Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Thu, 4 Feb 2016 13:07:56 -0500 Subject: [PATCH 16/83] streamline tests --- test/MMSP.unitTestSuite.cpp | 192 ++++++++++++++---------------------- 1 file changed, 72 insertions(+), 120 deletions(-) diff --git a/test/MMSP.unitTestSuite.cpp b/test/MMSP.unitTestSuite.cpp index fcac71b..54f8205 100644 --- a/test/MMSP.unitTestSuite.cpp +++ b/test/MMSP.unitTestSuite.cpp @@ -1,141 +1,94 @@ /* MMSP.unitTest.hpp -** Use Google Test to build unit tests for MMSP classes -** http://github.com/google/googletest -*/ + * Use Google Test to build unit tests for MMSP classes + * http://github.com/google/googletest + */ + #include"MMSP.hpp" #include +// GTest supports templated unit testing. Declare the types of interest. + using testing::Types; typedef Types datatypes; - - template -class scalarTest : public testing::Test +class dataTestSuite : public testing::Test { protected: virtual void SetUp() { - value = 2; - } - - MMSP::scalar value; -}; - -TYPED_TEST_CASE(scalarTest, datatypes); - -TYPED_TEST(scalarTest, testSize) { - EXPECT_EQ(this->value.buffer_size(),sizeof(TypeParam)); -} -TYPED_TEST(scalarTest, testAddition) { - EXPECT_EQ(this->value+this->value,static_cast(4)); -} -TYPED_TEST(scalarTest, testSquares) { - EXPECT_EQ(this->value*this->value,static_cast(4)); -} -TYPED_TEST(scalarTest, testMultiplication) { - EXPECT_EQ(2*this->value,static_cast(4)); - EXPECT_EQ(this->value*2,static_cast(4)); -} + myScalar = 2; - - - -template -class vectorTest : public testing::Test -{ -protected: - virtual void SetUp() { MMSP::vector temp(3,2); - value = temp; - } - - MMSP::vector value; -}; - -TYPED_TEST_CASE(vectorTest, datatypes); - -TYPED_TEST(vectorTest, testValue) { - for (int i=0; i<3; i++) { - EXPECT_EQ(this->value[i],static_cast(2)); - } -} -TYPED_TEST(vectorTest, testSize) { - EXPECT_EQ(this->value.length(),3); - EXPECT_EQ(this->value.buffer_size(),sizeof(TypeParam)); - EXPECT_DEATH(this->value[4],"index exceeds vector size"); -} -TYPED_TEST(vectorTest, testAddition) { - MMSP::vector temp = this->value+this->value; - for (int i=0; i<3; i++) - EXPECT_EQ(temp[i],static_cast(4)); -} -TYPED_TEST(vectorTest, testIncrement) { - this->value+=this->value; - for (int i=0; i<3; i++) - EXPECT_EQ(this->value[i],static_cast(4)); -} -TYPED_TEST(vectorTest, testSquares) { - EXPECT_EQ(this->value*this->value,12); -} -TYPED_TEST(vectorTest, testMultiplication) { - MMSP::vector a = 2*this->value; - //MMSP::vector b = this->value*2; - for (int i=0; i<3; i++) { - EXPECT_EQ(a[i],static_cast(4)); - //EXPECT_EQ(b[i],static_cast(4)); // no such operator - } -} + myVector = temp; - - - -template -class sparseTest : public testing::Test -{ -protected: - virtual void SetUp() { for (int i=0; i<3; i++) - value.set(2*i) = 2; + mySparse.set(2*i) = 2; } - - MMSP::sparse value; + MMSP::scalar myScalar; + MMSP::vector myVector; + MMSP::sparse mySparse; }; - -TYPED_TEST_CASE(sparseTest, datatypes); - -TYPED_TEST(sparseTest, testValue) { - for (int i=0; i<6; i+=2) - EXPECT_EQ(this->value[i],static_cast(2)); - for (int i=1; i<7; i+=2) - EXPECT_EQ(this->value[i],0); -} -TYPED_TEST(sparseTest, testSize) { - EXPECT_EQ(this->value.length(),3); - unsigned int delta = std::abs(sizeof(int) - sizeof(TypeParam)); - EXPECT_EQ(this->value.buffer_size(),sizeof(int) + 2*3*sizeof(TypeParam) + 3*delta); -} -TYPED_TEST(sparseTest, testAddition) { - MMSP::sparse temp = this->value+this->value; - for (int i=0; i<3; i++) - EXPECT_EQ(temp.value(i),static_cast(4)); -} -TYPED_TEST(sparseTest, testIncrement) { - this->value+=this->value; - for (int i=0; i<3; i++) - EXPECT_EQ(this->value.value(i),static_cast(4)); +TYPED_TEST_CASE(dataTestSuite, datatypes); + +TYPED_TEST(dataTestSuite, testSize) { + /*scalar*/ EXPECT_EQ(this->myScalar.buffer_size(),sizeof(TypeParam)); + /*vector*/ EXPECT_EQ(this->myVector.length(),3); + EXPECT_EQ(this->myVector.buffer_size(),sizeof(int)+3*sizeof(TypeParam)); + try { + this->myVector[4]; + FAIL(); + } catch(std::exception& err) { + EXPECT_STREQ("index exceeds vector size",err.what()); + } + /*sparse*/ EXPECT_EQ(this->mySparse.length(),3); + EXPECT_EQ(this->mySparse.buffer_size(),sizeof(int) + 6*std::max(sizeof(int),sizeof(TypeParam))); +} +TYPED_TEST(dataTestSuite, testValue) { + /*scalar*/ EXPECT_EQ(this->myScalar,static_cast(2)); + /*vector*/ for (int i=0; i<3; i++) + EXPECT_EQ(this->myVector[i],static_cast(2)); + /*sparse*/ for (int i=0; i<6; i+=2) + EXPECT_EQ(this->mySparse[i],static_cast(2)); + for (int i=1; i<7; i+=2) + EXPECT_EQ(this->mySparse[i],0); +} +TYPED_TEST(dataTestSuite, testAddition) { + /*scalar*/ EXPECT_EQ(this->myScalar+this->myScalar,static_cast(4)); + /*vector*/ MMSP::vector tempv = this->myVector+this->myVector; + for (int i=0; i<3; i++) + EXPECT_EQ(tempv[i],static_cast(4)); + /*sparse*/ MMSP::sparse temps = this->mySparse+this->mySparse; + for (int i=0; i<3; i++) + EXPECT_EQ(temps.value(i),static_cast(4)); +} +TYPED_TEST(dataTestSuite, testIncrement) { + /*scalar*/ this->myScalar += this->myScalar; + EXPECT_EQ(this->myScalar,static_cast(4)); + /*vector*/ this->myVector += this->myVector; + for (int i=0; i<3; i++) + EXPECT_EQ(this->myVector[i],static_cast(4)); + /*sparse*/ this->mySparse+=this->mySparse; + for (int i=0; i<3; i++) + EXPECT_EQ(this->mySparse.value(i),static_cast(4)); +} +TYPED_TEST(dataTestSuite, testSquares) { + /*scalar*/ EXPECT_EQ(this->myScalar*this->myScalar,static_cast(4)); + /*vector*/ EXPECT_EQ(this->myVector*this->myVector,12); + /*sparse*/ // No such operator +} +TYPED_TEST(dataTestSuite, testMultiplication) { + /*scalar*/ EXPECT_EQ(2*this->myScalar,static_cast(4)); + EXPECT_EQ(this->myScalar*2,static_cast(4)); + /*vector*/ MMSP::vector tempv = 2*this->myVector; + //MMSP::vector b = this->myVector*2; // no such operator + for (int i=0; i<3; i++) + EXPECT_EQ(tempv[i],static_cast(4)); + /*sparse*/ MMSP::sparse temps = 2*this->mySparse; + //MMSP::sparse b = this->mySparse*2; // no such operator + for (int i=0; i<3; i++) + EXPECT_EQ(temps.value(i),static_cast(4)); } -TYPED_TEST(sparseTest, testMultiplication) { - MMSP::sparse a = 2*this->value; - //MMSP::sparse b = this->value*2; - for (int i=0; i<3; i++) { - EXPECT_EQ(a.value(i),static_cast(4)); - //EXPECT_EQ(b.value(i),static_cast(4)); // no such operator - } -} - - - template @@ -170,4 +123,3 @@ TYPED_TEST(gridTest, testSize) { EXPECT_EQ(this->grid1D.buffer_size(),MMSP::nodes(this->grid1D)*sizeof(TypeParam)); } - From 423d90d9b414270aaf2339af60375490f07dffa6 Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Thu, 4 Feb 2016 14:01:46 -0500 Subject: [PATCH 17/83] add operator* for vector,scalar and sparse,scalar --- include/MMSP.sparse.hpp | 4 +++- include/MMSP.vector.hpp | 3 +++ test/MMSP.unitTestSuite.cpp | 22 +++++++++++----------- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/include/MMSP.sparse.hpp b/include/MMSP.sparse.hpp index 0221cfd..00374c7 100644 --- a/include/MMSP.sparse.hpp +++ b/include/MMSP.sparse.hpp @@ -321,7 +321,9 @@ template sparse operator*(const U& value, const spar z.set(x.index(i)) = static_cast(value) * x.value(i); return z; } - +template sparse operator*(const sparse& x, const U& value) { + return value*x; +} // target class: dim = 0 specialization for sparse class template diff --git a/include/MMSP.vector.hpp b/include/MMSP.vector.hpp index 9ee03c9..9838099 100644 --- a/include/MMSP.vector.hpp +++ b/include/MMSP.vector.hpp @@ -305,6 +305,9 @@ template vector operator*(const U& value, const vect for (int i = 0; i < N; i++) z[i] = static_cast(value) * x[i]; return z; } +template vector operator*(const vector& x, const U& value) { + return value*x; +} template T operator*(const vector& x, const vector& y) { int N = (x.length()>y.length())?y.length():x.length(); T z(0); diff --git a/test/MMSP.unitTestSuite.cpp b/test/MMSP.unitTestSuite.cpp index 54f8205..5aeb8ef 100644 --- a/test/MMSP.unitTestSuite.cpp +++ b/test/MMSP.unitTestSuite.cpp @@ -35,12 +35,8 @@ TYPED_TEST(dataTestSuite, testSize) { /*scalar*/ EXPECT_EQ(this->myScalar.buffer_size(),sizeof(TypeParam)); /*vector*/ EXPECT_EQ(this->myVector.length(),3); EXPECT_EQ(this->myVector.buffer_size(),sizeof(int)+3*sizeof(TypeParam)); - try { - this->myVector[4]; - FAIL(); - } catch(std::exception& err) { - EXPECT_STREQ("index exceeds vector size",err.what()); - } + EXPECT_THROW(this->myVector[4],std::out_of_range); + /*sparse*/ EXPECT_EQ(this->mySparse.length(),3); EXPECT_EQ(this->mySparse.buffer_size(),sizeof(int) + 6*std::max(sizeof(int),sizeof(TypeParam))); } @@ -75,19 +71,23 @@ TYPED_TEST(dataTestSuite, testIncrement) { TYPED_TEST(dataTestSuite, testSquares) { /*scalar*/ EXPECT_EQ(this->myScalar*this->myScalar,static_cast(4)); /*vector*/ EXPECT_EQ(this->myVector*this->myVector,12); - /*sparse*/ // No such operator + /*sparse*/ // No operator*(sparse,sparse): meaning is not obvious. } TYPED_TEST(dataTestSuite, testMultiplication) { /*scalar*/ EXPECT_EQ(2*this->myScalar,static_cast(4)); EXPECT_EQ(this->myScalar*2,static_cast(4)); /*vector*/ MMSP::vector tempv = 2*this->myVector; - //MMSP::vector b = this->myVector*2; // no such operator - for (int i=0; i<3; i++) + MMSP::vector tempv2 = this->myVector*2; + for (int i=0; i<3; i++) { EXPECT_EQ(tempv[i],static_cast(4)); + EXPECT_EQ(tempv2[i],static_cast(4)); + } /*sparse*/ MMSP::sparse temps = 2*this->mySparse; - //MMSP::sparse b = this->mySparse*2; // no such operator - for (int i=0; i<3; i++) + MMSP::sparse temps2 = this->mySparse*2; + for (int i=0; i<3; i++) { EXPECT_EQ(temps.value(i),static_cast(4)); + EXPECT_EQ(temps2.value(i),static_cast(4)); + } } From aa99f42aa85d78a596851ebbcb1d3dd817ace38a Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Thu, 4 Feb 2016 14:49:54 -0500 Subject: [PATCH 18/83] take ownership of main, parallel test suite, streamline builds --- ...itTestSuite.cpp => MMSP.unitTestSuite.hpp} | 0 test/Makefile | 53 +++++++++---------- 2 files changed, 25 insertions(+), 28 deletions(-) rename test/{MMSP.unitTestSuite.cpp => MMSP.unitTestSuite.hpp} (100%) diff --git a/test/MMSP.unitTestSuite.cpp b/test/MMSP.unitTestSuite.hpp similarity index 100% rename from test/MMSP.unitTestSuite.cpp rename to test/MMSP.unitTestSuite.hpp diff --git a/test/Makefile b/test/Makefile index c572de7..ab29857 100644 --- a/test/Makefile +++ b/test/Makefile @@ -1,58 +1,55 @@ # Makefile # GNU makefile for single and parallel test programs using MMSP -# and the Google unit test framework (gtest) -# Questions/comments to gruberja@gmail.com (Jason Gruber) +# Questions/comments to trevor.keller@gmail.com (Trevor Keller) # includes incdir = ../include -gtdir = gtest/googletest # compilers/flags compiler = g++ flags = -g -I$(incdir) -gflags = -isystem $(gtdir)/include -I$(gtdir) $(flags) -I$(gtdir)/include -pthread pcompiler = mpic++ pflags = $(flags) -include mpi.h -# dependencies -core = $(incdir)/MMSP.hpp \ - $(incdir)/MMSP.utility.hpp \ - $(incdir)/MMSP.grid.hpp \ - $(incdir)/MMSP.scalar.hpp \ - $(incdir)/MMSP.vector.hpp \ - $(incdir)/MMSP.sparse.hpp - -# the program -test: test.cpp $(core) +# Hello World program +test: test.cpp $(compiler) $(flags) $< -o $@ -parallel: test.cpp $(core) +parallel: test.cpp $(pcompiler) $(pflags) $< -o $@ -# GTEST +# Unit testing depends on the Google C++ testing framework, gtest. +# This is introduced as a git submodule in the gtest sub-directory. +# If yours is a fresh clone of MMSP, you can populate this module +# from the top-level directory by executing +# $ git submodule init +# $ git submodule update +# Alternatively, clone MMSP recursively: +# $ git clone --recursive https://github.com/mesoscale/mmsp.git + +gtdir = gtest/googletest + +gflags = -isystem $(gtdir)/include -I$(gtdir) $(flags) \ + -I$(gtdir)/include -pthread + gthdr = $(gtdir)/include/gtest/*.h \ $(gtdir)/include/gtest/internal/*.h gtsrc = $(gtdir)/src/*.cc \ $(gtdir)/src/*.h +# gtest dependencies gtest-all.o: $(gtsrc) $(gthdr) $(compiler) $(gflags) -c $(gtdir)/src/gtest-all.cc -gtest_main.o: $(gtsrc) $(gthdr) - $(compiler) $(gflags) -c $(gtdir)/src/gtest_main.cc - -gtest_main.a: gtest-all.o gtest_main.o - $(AR) $(ARFLAGS) $@ $^ - -MMSP.unitTestSuite.o: $(core) $(gthdr) - $(compiler) $(gflags) -c MMSP.unitTestSuite.cpp +# unit testing program +unitTestSuite: MMSP.unitTestSuite.cpp gtest-all.o + $(compiler) $(gflags) $^ -o $@ -lpthread -lz -unitTestSuite: MMSP.unitTestSuite.o gtest_main.a - $(compiler) $(gflags) $^ -o $@ -lpthread -lz && \ - ./$@ +parallelTestSuite: MMSP.unitTestSuite.cpp gtest-all.o + $(pcompiler) $(gflags) -include mpi.h $^ -o $@ -lpthread -lz clean: - rm -f test parallel unitTestSuite *.a *.o + rm -f test parallel unitTestSuite parallelTestSuite gtest-all.o From cf66ec8436ecc3a3cdbada6095deedcbaa7b51c2 Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Thu, 4 Feb 2016 14:49:54 -0500 Subject: [PATCH 19/83] take ownership of main, parallel test suite, streamline builds --- ...itTestSuite.cpp => MMSP.unitTestSuite.hpp} | 2 +- test/Makefile | 53 +++++++++---------- 2 files changed, 26 insertions(+), 29 deletions(-) rename test/{MMSP.unitTestSuite.cpp => MMSP.unitTestSuite.hpp} (98%) diff --git a/test/MMSP.unitTestSuite.cpp b/test/MMSP.unitTestSuite.hpp similarity index 98% rename from test/MMSP.unitTestSuite.cpp rename to test/MMSP.unitTestSuite.hpp index 5aeb8ef..98d971d 100644 --- a/test/MMSP.unitTestSuite.cpp +++ b/test/MMSP.unitTestSuite.hpp @@ -118,7 +118,7 @@ TYPED_TEST(gridTest, testSize) { EXPECT_EQ(MMSP::nodes(this->grid1D),64); #else int np = MPI::COMM_WORLD.Get_size(); - EXPECT_LE(MMSP::nodes(this->grid1D),64/np); + EXPECT_LE(MMSP::nodes(this->grid1D),1+64/np); #endif EXPECT_EQ(this->grid1D.buffer_size(),MMSP::nodes(this->grid1D)*sizeof(TypeParam)); } diff --git a/test/Makefile b/test/Makefile index c572de7..ab29857 100644 --- a/test/Makefile +++ b/test/Makefile @@ -1,58 +1,55 @@ # Makefile # GNU makefile for single and parallel test programs using MMSP -# and the Google unit test framework (gtest) -# Questions/comments to gruberja@gmail.com (Jason Gruber) +# Questions/comments to trevor.keller@gmail.com (Trevor Keller) # includes incdir = ../include -gtdir = gtest/googletest # compilers/flags compiler = g++ flags = -g -I$(incdir) -gflags = -isystem $(gtdir)/include -I$(gtdir) $(flags) -I$(gtdir)/include -pthread pcompiler = mpic++ pflags = $(flags) -include mpi.h -# dependencies -core = $(incdir)/MMSP.hpp \ - $(incdir)/MMSP.utility.hpp \ - $(incdir)/MMSP.grid.hpp \ - $(incdir)/MMSP.scalar.hpp \ - $(incdir)/MMSP.vector.hpp \ - $(incdir)/MMSP.sparse.hpp - -# the program -test: test.cpp $(core) +# Hello World program +test: test.cpp $(compiler) $(flags) $< -o $@ -parallel: test.cpp $(core) +parallel: test.cpp $(pcompiler) $(pflags) $< -o $@ -# GTEST +# Unit testing depends on the Google C++ testing framework, gtest. +# This is introduced as a git submodule in the gtest sub-directory. +# If yours is a fresh clone of MMSP, you can populate this module +# from the top-level directory by executing +# $ git submodule init +# $ git submodule update +# Alternatively, clone MMSP recursively: +# $ git clone --recursive https://github.com/mesoscale/mmsp.git + +gtdir = gtest/googletest + +gflags = -isystem $(gtdir)/include -I$(gtdir) $(flags) \ + -I$(gtdir)/include -pthread + gthdr = $(gtdir)/include/gtest/*.h \ $(gtdir)/include/gtest/internal/*.h gtsrc = $(gtdir)/src/*.cc \ $(gtdir)/src/*.h +# gtest dependencies gtest-all.o: $(gtsrc) $(gthdr) $(compiler) $(gflags) -c $(gtdir)/src/gtest-all.cc -gtest_main.o: $(gtsrc) $(gthdr) - $(compiler) $(gflags) -c $(gtdir)/src/gtest_main.cc - -gtest_main.a: gtest-all.o gtest_main.o - $(AR) $(ARFLAGS) $@ $^ - -MMSP.unitTestSuite.o: $(core) $(gthdr) - $(compiler) $(gflags) -c MMSP.unitTestSuite.cpp +# unit testing program +unitTestSuite: MMSP.unitTestSuite.cpp gtest-all.o + $(compiler) $(gflags) $^ -o $@ -lpthread -lz -unitTestSuite: MMSP.unitTestSuite.o gtest_main.a - $(compiler) $(gflags) $^ -o $@ -lpthread -lz && \ - ./$@ +parallelTestSuite: MMSP.unitTestSuite.cpp gtest-all.o + $(pcompiler) $(gflags) -include mpi.h $^ -o $@ -lpthread -lz clean: - rm -f test parallel unitTestSuite *.a *.o + rm -f test parallel unitTestSuite parallelTestSuite gtest-all.o From 5fd8fdbc7ed58b0200fd48fc9fe76118b6171b46 Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Fri, 12 Feb 2016 12:41:09 -0500 Subject: [PATCH 20/83] appropriate srand placement srand must be called before rand, and should be seeded differently on each MPI rank to avoid duplication of the pattern on each parallel subdomain. Since rand is implemented with static variables at global scope, it must be called once -- and only once. Therefore, rather than inside generate or update, main is the appropriate place for the srand(time(NULL)+rank) function call. --- .../anisotropic/Monte_Carlo/graingrowth.cpp | 3 ++- .../grain_growth/isotropic/Monte_Carlo/graingrowth.cpp | 3 ++- .../ostwald_ripening/isotropic/phase_field/ostwald.cpp | 3 ++- .../zener_pinning/anisotropic/Monte_Carlo/zener.cpp | 2 ++ .../zener_pinning/anisotropic/phase_field/zener.cpp | 1 + .../zener_pinning/anisotropic/sparsePF/zener.cpp | 1 + .../zener_pinning/isotropic/Monte_Carlo/zener.cpp | 2 ++ .../zener_pinning/isotropic/phase_field/zener.cpp | 1 + .../zener_pinning/isotropic/sparsePF/zener.cpp | 1 + examples/phase_transitions/allen-cahn/allen-cahn.cpp | 1 + .../cahn-hilliard/explicit/cahn-hilliard.cpp | 1 + examples/phase_transitions/model_A/model_A.cpp | 1 + examples/phase_transitions/model_B/model_B.cpp | 1 + examples/phase_transitions/spinodal/spinodal.cpp | 1 + .../statistical_mechanics/Heisenberg/heisenberg.cpp | 2 ++ examples/statistical_mechanics/Ising/ising.cpp | 2 ++ examples/statistical_mechanics/Potts/potts.cpp | 2 ++ include/MMSP.main.hpp | 10 ++++++++-- 18 files changed, 33 insertions(+), 5 deletions(-) diff --git a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp index 267008d..49e09db 100644 --- a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp +++ b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp @@ -18,6 +18,7 @@ namespace MMSP { void generate(int dim, const char* filename) { + // srand() is called exactly once in MMSP.main.hpp. Do not call it here. if (dim == 1) { GRID1D initGrid(0, 0, 128); @@ -180,8 +181,8 @@ template void update(grid& mcGrid, int steps) else if (dim == 3) num_sublattices = 8; for (int sublattice = 0; sublattice < num_sublattices; sublattice++) { - srand(time(NULL)); /* seed random number generator */ vector x (dim, 0); + // srand() is called exactly once in MMSP.main.hpp. Do not call it here. for (int hh = 0; hh < num_grids_to_flip[sublattice]; hh++) { int cell_numbering = rand() % (number_of_lattice_cells); //choose a cell to flip, from 0 to num_cells_in_thread-1 diff --git a/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp b/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp index e002f96..73c9f04 100644 --- a/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp +++ b/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp @@ -17,6 +17,7 @@ namespace MMSP { void generate(int dim, const char* filename) { + // srand() is called exactly once in MMSP.main.hpp. Do not call it here. if (dim == 1) { GRID1D initGrid(0, 0, 128); @@ -174,8 +175,8 @@ template void update(grid& mcGrid, int steps) else if (dim == 3) num_of_sublattices = 8; for (int sublattice = 0; sublattice < num_of_sublattices; sublattice++) { - srand(time(NULL)); /* seed random number generator */ vector x (dim, 0); + // srand() is called exactly once in MMSP.main.hpp. Do not call it here. for (int hh = 0; hh < num_of_grids_to_flip[sublattice]; hh++) { int cell_numbering = rand() % (number_of_lattice_cells); //choose a cell to flip, from 0 to num_of_cells_in_thread-1 diff --git a/examples/coarsening/ostwald_ripening/isotropic/phase_field/ostwald.cpp b/examples/coarsening/ostwald_ripening/isotropic/phase_field/ostwald.cpp index fce3ae6..d03362f 100644 --- a/examples/coarsening/ostwald_ripening/isotropic/phase_field/ostwald.cpp +++ b/examples/coarsening/ostwald_ripening/isotropic/phase_field/ostwald.cpp @@ -12,6 +12,7 @@ namespace MMSP{ void generate(int dim, const char* filename) { + // srand() is called exactly once in MMSP.main.hpp. Do not call it here. if (dim==1) { GRID1D initGrid(2,0,128); @@ -59,7 +60,7 @@ template void update(grid >& oldGrid, int st grid > newGrid(oldGrid); grid wspace(oldGrid,1); - double dt = 0.01; + double dt = 0.001; double L = 1.0; double D = 1.0; diff --git a/examples/coarsening/zener_pinning/anisotropic/Monte_Carlo/zener.cpp b/examples/coarsening/zener_pinning/anisotropic/Monte_Carlo/zener.cpp index 7d9fed6..9119e3d 100644 --- a/examples/coarsening/zener_pinning/anisotropic/Monte_Carlo/zener.cpp +++ b/examples/coarsening/zener_pinning/anisotropic/Monte_Carlo/zener.cpp @@ -13,6 +13,7 @@ namespace MMSP{ void generate(int dim, const char* filename) { + // srand() is called exactly once in MMSP.main.hpp. Do not call it here. if (dim==1) { GRID1D initGrid(0,0,128); @@ -90,6 +91,7 @@ template void update(grid& mcGrid, int steps) const double kT = (dim==3)?0.75:0.50; int gss = int(nodes(mcGrid)); + // srand() is called exactly once in MMSP.main.hpp. Do not call it here. for (int step=0; step void update(grid& spinGrid, int steps) const double kT = (dim==3)?0.75:0.50; int gss = int(sqrt(nodes(spinGrid))); + // srand() is called exactly once in MMSP.main.hpp. Do not call it here. for (int step=0; step void update(grid >& spinGrid, int s double pi = acos(-1.0); int gss = (dim==1)?nodes(spinGrid):int(sqrt(nodes(spinGrid))); + // srand() is called exactly once in MMSP.main.hpp. Do not call it here. for (int step=0; step void update(grid& spinGrid, int steps) double kT = (dim==2)?0.50:0.75; int gss = (dim==1)?nodes(spinGrid):int(sqrt(nodes(spinGrid))); + // srand() is called exactly once in MMSP.main.hpp. Do not call it here. for (int step=0; step void update(grid& spinGrid, int steps) double kT = (dim==3)?0.75:0.50; int gss = (dim==1)?nodes(spinGrid):int(sqrt(nodes(spinGrid))); + // srand() is called exactly once in MMSP.main.hpp. Do not call it here. for (int step=0; step #include #include +#include int main(int argc, char* argv[]) { MMSP::Init(argc, argv); - // check argument list if (argc < 2) { std::cout << PROGRAM << ": bad argument list. Use\n\n"; @@ -46,6 +46,13 @@ int main(int argc, char* argv[]) { exit(-1); } + // Many of the example programs call rand(). srand() must be called + // _exactly once_, making this the proper place for it. + int rank = 0; + #ifdef MPI_VERSION + rank = MPI::COMM_WORLD.Get_rank(); + #endif + srand(time(NULL)+rank); // print help message and exit if (std::string(argv[1]) == std::string("--help")) { @@ -81,7 +88,6 @@ int main(int argc, char* argv[]) { exit(0); } - // generate example grid else if (std::string(argv[1]) == std::string("--example")) { // check argument list From fd48a8401601d90f9e6c081265763a6ce928cce0 Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Fri, 12 Feb 2016 15:59:25 -0500 Subject: [PATCH 21/83] use doubles for examples MMSP is templated to simplify experiments with any arbitrary data types, but our examples should exhibit best practices. For scientific computing, that means preferring double precision over single. --- examples/beginners_diffusion/1stDiffusion.cpp | 6 +++--- examples/beginners_diffusion/MMSPDiffusion.cpp | 4 ++-- examples/beginners_diffusion/MMSPDiffusion2D.cpp | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/beginners_diffusion/1stDiffusion.cpp b/examples/beginners_diffusion/1stDiffusion.cpp index d91587a..a4c9933 100644 --- a/examples/beginners_diffusion/1stDiffusion.cpp +++ b/examples/beginners_diffusion/1stDiffusion.cpp @@ -25,9 +25,9 @@ int main() std::cin>>iterate; std::cout<<""< > GRID(1,0,length); - grid<1,scalar > GRID2(1,0,length); + //here we define some 1 dimensional grids with double variable types + grid<1,scalar > GRID(1,0,length); + grid<1,scalar > GRID2(1,0,length); //this value is defined for looping control offlength=x1(GRID)-3; diff --git a/examples/beginners_diffusion/MMSPDiffusion.cpp b/examples/beginners_diffusion/MMSPDiffusion.cpp index ce1cfa6..02032ac 100644 --- a/examples/beginners_diffusion/MMSPDiffusion.cpp +++ b/examples/beginners_diffusion/MMSPDiffusion.cpp @@ -19,8 +19,8 @@ int main() std::cin>>iterate; std::cout<<""< > GRID(1,0,length); - grid<1,scalar > update(1,0,length); + grid<1,scalar > GRID(1,0,length); + grid<1,scalar > update(1,0,length); for (int x=x0(GRID); x>iterate; std::cout<<""< > GRID(1,0,length,0,length); - grid<2,scalar > update(1,0,length,0,length); + grid<2,scalar > GRID(1,0,length,0,length); + grid<2,scalar > update(1,0,length,0,length); for (int x=x0(GRID); x Date: Fri, 12 Feb 2016 16:50:14 -0500 Subject: [PATCH 22/83] add timer to build check --- test/build_examples.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/test/build_examples.sh b/test/build_examples.sh index 37dbb68..83c5ed8 100755 --- a/test/build_examples.sh +++ b/test/build_examples.sh @@ -1,5 +1,7 @@ #!/bin/bash +tstart=$(date +%s) + cd ../examples while [[ $# > 0 ]] @@ -220,7 +222,10 @@ pwd if $CLEAN; then make -s clean; fi cd $examples +tfinish=$(date +%s) +elapsed=$(echo "$tfinish-$tstart" | bc -l) + echo -echo "Examples built successfully." +echo "Examples built successfully. ${elapsed} seconds elapsed." cd ../test/ From 20b3ac75e732fd54e1f15112774d07ff50cef4eb Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Sat, 13 Feb 2016 18:18:42 -0500 Subject: [PATCH 23/83] build all beginner_diffusion examples, call Init, add return statements --- examples/beginners_diffusion/1stDiffusion.cpp | 7 ++++- .../beginners_diffusion/MMSPDiffusion.cpp | 4 ++- .../beginners_diffusion/MMSPDiffusion2D.cpp | 4 ++- examples/beginners_diffusion/Makefile | 29 ++++++++++++++----- 4 files changed, 34 insertions(+), 10 deletions(-) diff --git a/examples/beginners_diffusion/1stDiffusion.cpp b/examples/beginners_diffusion/1stDiffusion.cpp index a4c9933..4961f08 100644 --- a/examples/beginners_diffusion/1stDiffusion.cpp +++ b/examples/beginners_diffusion/1stDiffusion.cpp @@ -7,8 +7,11 @@ using namespace MMSP; // Make it more flexible for documentation (get the n-dimension thing down) -int main() +int main(int argc, char* argv[]) { + // Since we construct grid objects, we must initialize MMSP + Init(argc, argv); + //Here is where all variables are called. In c++ all variables must be defined before the can be used. //The first word is the data type associated with the vairable. int length; @@ -62,6 +65,8 @@ int main() for (int x=x0(GRID); x Date: Sat, 13 Feb 2016 18:37:49 -0500 Subject: [PATCH 24/83] read-only gtest module, hhtps instead of ssh --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index 0a27a17..3c25e3e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "test/gtest"] path = test/gtest - url = git@github.com:google/googletest.git + url = https://github.com/google/googletest.git From 1c796782528aab63980d23d977482adec2d290f4 Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Sat, 13 Feb 2016 18:41:39 -0500 Subject: [PATCH 25/83] unit testing main with MMSP init --- .gitignore | 1 - test/MMSP.unitTestSuite.cpp | 42 +++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 test/MMSP.unitTestSuite.cpp diff --git a/.gitignore b/.gitignore index 60fb49f..9d278e3 100644 --- a/.gitignore +++ b/.gitignore @@ -26,7 +26,6 @@ utility/vox2MC utility/wrongendian # unit testing -test/MMSP.unitTestSuite.cpp test/unitTestSuite # Compiled Object files diff --git a/test/MMSP.unitTestSuite.cpp b/test/MMSP.unitTestSuite.cpp new file mode 100644 index 0000000..910e57f --- /dev/null +++ b/test/MMSP.unitTestSuite.cpp @@ -0,0 +1,42 @@ +// Copyright 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include + +#include "gtest/gtest.h" +#include"MMSP.unitTestSuite.hpp" + +int main(int argc, char* argv[]) { + MMSP::Init(argc, argv); + printf("Running main() from gtest_main.cc\n"); + testing::InitGoogleTest(&argc, argv); + int result = RUN_ALL_TESTS(); + MMSP::Finalize(); + return result; +} From 1919b0ca9c7ab3af7cb85319da7195dad1efbd5c Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Sat, 13 Feb 2016 21:57:57 -0500 Subject: [PATCH 26/83] exploring example testing with graingrowth --- .../isotropic/phase_field/graingrowth.cpp | 4 + test/MMSP.exampleTestSuite.cpp | 47 ++++++++++ test/MMSP.exampleTestSuite.hpp | 94 +++++++++++++++++++ test/Makefile | 13 ++- 4 files changed, 154 insertions(+), 4 deletions(-) create mode 100644 test/MMSP.exampleTestSuite.cpp create mode 100644 test/MMSP.exampleTestSuite.hpp diff --git a/examples/coarsening/grain_growth/isotropic/phase_field/graingrowth.cpp b/examples/coarsening/grain_growth/isotropic/phase_field/graingrowth.cpp index 03cf03d..d649ec1 100644 --- a/examples/coarsening/grain_growth/isotropic/phase_field/graingrowth.cpp +++ b/examples/coarsening/grain_growth/isotropic/phase_field/graingrowth.cpp @@ -78,8 +78,10 @@ template void update(grid >& oldGrid, int st double dt = 0.01; for (int step=0; step lap = laplacian(oldGrid,i); @@ -106,4 +108,6 @@ template void update(grid >& oldGrid, int st #endif +#ifndef UNITTESTING #include"MMSP.main.hpp" +#endif diff --git a/test/MMSP.exampleTestSuite.cpp b/test/MMSP.exampleTestSuite.cpp new file mode 100644 index 0000000..682e879 --- /dev/null +++ b/test/MMSP.exampleTestSuite.cpp @@ -0,0 +1,47 @@ +// Copyright 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include + +#include "gtest/gtest.h" +#include"MMSP.exampleTestSuite.hpp" + +int main(int argc, char* argv[]) { + MMSP::Init(argc, argv); + testing::InitGoogleTest(&argc, argv); + int rank=0; + #ifdef MPI_VERSION + rank=MPI::COMM_WORLD.Get_rank(); + #endif + srand(time(NULL)+rank); + int result = RUN_ALL_TESTS(); + MMSP::Finalize(); + return result; +} diff --git a/test/MMSP.exampleTestSuite.hpp b/test/MMSP.exampleTestSuite.hpp new file mode 100644 index 0000000..336bba3 --- /dev/null +++ b/test/MMSP.exampleTestSuite.hpp @@ -0,0 +1,94 @@ +/* MMSP.unitTest.hpp + * Use Google Test to build unit tests for MMSP classes + * http://github.com/google/googletest + */ + +#include"MMSP.hpp" +#include +#include + +// GTest supports templated unit testing. Declare the types of interest. + +//using testing::Types; +//typedef Types datatypes; + +TEST(grid1DTest, Finite) { + MMSP::vector length(3,0.0); + MMSP::generate(1, "short1.0000.dat"); + GRID1D myGrid1D("short1.0000.dat"); + for (int n=0; n,myGrid1D(n)[i]); + for (int i=1; i<3; i++) + EXPECT_GE(length[i-1],length[i]); +} + +TEST(grid2DTest, Finite) { + MMSP::vector area(3,0.0); + MMSP::generate(2, "short2.0000.dat"); + GRID2D myGrid2D("short2.0000.dat"); + for (int n=0; n,myGrid2D(n)[i]); + for (int i=1; i<3; i++) + EXPECT_GE(area[i-1],area[i]); +} + +TEST(grid3DTest, Finite) { + MMSP::vector volume(3,0.0); + MMSP::generate(3, "short3.0000.dat"); + GRID3D myGrid3D("short3.0000.dat"); + for (int n=0; n,myGrid3D(n)[i]); + for (int i=1; i<3; i++) + EXPECT_GE(volume[i-1],volume[i]); +} diff --git a/test/Makefile b/test/Makefile index ab29857..d927a5b 100644 --- a/test/Makefile +++ b/test/Makefile @@ -7,7 +7,7 @@ incdir = ../include # compilers/flags compiler = g++ -flags = -g -I$(incdir) +flags = -O2 -I$(incdir) pcompiler = mpic++ pflags = $(flags) -include mpi.h @@ -19,7 +19,6 @@ parallel: test.cpp $(pcompiler) $(pflags) $< -o $@ - # Unit testing depends on the Google C++ testing framework, gtest. # This is introduced as a git submodule in the gtest sub-directory. # If yours is a fresh clone of MMSP, you can populate this module @@ -31,8 +30,8 @@ parallel: test.cpp gtdir = gtest/googletest -gflags = -isystem $(gtdir)/include -I$(gtdir) $(flags) \ - -I$(gtdir)/include -pthread +gflags = -isystem $(gtdir)/include \ + -I$(gtdir) $(flags) -I$(gtdir)/include -pthread gthdr = $(gtdir)/include/gtest/*.h \ $(gtdir)/include/gtest/internal/*.h @@ -51,5 +50,11 @@ unitTestSuite: MMSP.unitTestSuite.cpp gtest-all.o parallelTestSuite: MMSP.unitTestSuite.cpp gtest-all.o $(pcompiler) $(gflags) -include mpi.h $^ -o $@ -lpthread -lz +# example testing program +exampleTestSuite: MMSP.exampleTestSuite.cpp gtest-all.o + $(compiler) $(gflags) -DUNITTESTING \ + -include ../examples/coarsening/grain_growth/isotropic/phase_field/graingrowth.cpp \ + $^ -o $@ -lpthread -lz + clean: rm -f test parallel unitTestSuite parallelTestSuite gtest-all.o From 7567a67f1459ae5c099184782bd07ceccc2fbcfd Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Sat, 13 Feb 2016 23:36:18 -0500 Subject: [PATCH 27/83] unit tests alongside implementation --- .../isotropic/phase_field/graingrowth.cpp | 75 +++++++++++++++ test/MMSP.exampleTestSuite.cpp | 7 +- test/MMSP.exampleTestSuite.hpp | 94 ------------------- 3 files changed, 78 insertions(+), 98 deletions(-) delete mode 100644 test/MMSP.exampleTestSuite.hpp diff --git a/examples/coarsening/grain_growth/isotropic/phase_field/graingrowth.cpp b/examples/coarsening/grain_growth/isotropic/phase_field/graingrowth.cpp index d649ec1..b0a6e46 100644 --- a/examples/coarsening/grain_growth/isotropic/phase_field/graingrowth.cpp +++ b/examples/coarsening/grain_growth/isotropic/phase_field/graingrowth.cpp @@ -109,5 +109,80 @@ template void update(grid >& oldGrid, int st #endif #ifndef UNITTESTING + #include"MMSP.main.hpp" + +#else + +/* validation with Google Test suite + * http://github.com/google/googletest + */ +#include + +TEST(grid1DTest, Finite) { + MMSP::vector length(2,0.0); + MMSP::generate(1, "short1.00000.dat"); + GRID1D myGrid1D("short1.00000.dat"); + for (int n=0; n,myGrid1D(n)[i]); + EXPECT_NEAR(length[1],length[0],1.0); +} + +TEST(grid2DTest, Finite) { + MMSP::vector area(2,0.0); + MMSP::generate(2, "short2.0000.dat"); + GRID2D myGrid2D("short2.0000.dat"); + for (int n=0; n,myGrid2D(n)[i]); + EXPECT_LE(area[1],area[0]); +} + +TEST(grid3DTest, Finite) { + MMSP::vector volume(2,0.0); + MMSP::generate(3, "short3.0000.dat"); + GRID3D myGrid3D("short3.0000.dat"); + for (int n=0; n,myGrid3D(n)[i]); + EXPECT_LE(volume[1],volume[0]); +} + #endif diff --git a/test/MMSP.exampleTestSuite.cpp b/test/MMSP.exampleTestSuite.cpp index 682e879..543275b 100644 --- a/test/MMSP.exampleTestSuite.cpp +++ b/test/MMSP.exampleTestSuite.cpp @@ -27,11 +27,10 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#include +#include #include - -#include "gtest/gtest.h" -#include"MMSP.exampleTestSuite.hpp" +#include +//#include"MMSP.exampleTestSuite.hpp" int main(int argc, char* argv[]) { MMSP::Init(argc, argv); diff --git a/test/MMSP.exampleTestSuite.hpp b/test/MMSP.exampleTestSuite.hpp deleted file mode 100644 index 336bba3..0000000 --- a/test/MMSP.exampleTestSuite.hpp +++ /dev/null @@ -1,94 +0,0 @@ -/* MMSP.unitTest.hpp - * Use Google Test to build unit tests for MMSP classes - * http://github.com/google/googletest - */ - -#include"MMSP.hpp" -#include -#include - -// GTest supports templated unit testing. Declare the types of interest. - -//using testing::Types; -//typedef Types datatypes; - -TEST(grid1DTest, Finite) { - MMSP::vector length(3,0.0); - MMSP::generate(1, "short1.0000.dat"); - GRID1D myGrid1D("short1.0000.dat"); - for (int n=0; n,myGrid1D(n)[i]); - for (int i=1; i<3; i++) - EXPECT_GE(length[i-1],length[i]); -} - -TEST(grid2DTest, Finite) { - MMSP::vector area(3,0.0); - MMSP::generate(2, "short2.0000.dat"); - GRID2D myGrid2D("short2.0000.dat"); - for (int n=0; n,myGrid2D(n)[i]); - for (int i=1; i<3; i++) - EXPECT_GE(area[i-1],area[i]); -} - -TEST(grid3DTest, Finite) { - MMSP::vector volume(3,0.0); - MMSP::generate(3, "short3.0000.dat"); - GRID3D myGrid3D("short3.0000.dat"); - for (int n=0; n,myGrid3D(n)[i]); - for (int i=1; i<3; i++) - EXPECT_GE(volume[i-1],volume[i]); -} From d05182cfcdd5f094d2c8365f51c333144471d4f1 Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Sun, 14 Feb 2016 15:20:23 -0500 Subject: [PATCH 28/83] run 2D tests, generate images --- .../cahn-hilliard/convex_splitting/Makefile | 2 +- test/build_examples.sh | 48 +++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/examples/phase_transitions/cahn-hilliard/convex_splitting/Makefile b/examples/phase_transitions/cahn-hilliard/convex_splitting/Makefile index 49adbe1..ee4abb3 100644 --- a/examples/phase_transitions/cahn-hilliard/convex_splitting/Makefile +++ b/examples/phase_transitions/cahn-hilliard/convex_splitting/Makefile @@ -7,7 +7,7 @@ incdir = ../../../../include # compilers/flags -compiler = icc +compiler = g++ flags = -O3 -DVANILLA -Wall -std=c++11 -I $(incdir) gflags = -pg -O2 -DVANILLA -Wall -std=c++11 -I $(incdir) pcompiler = mpic++ diff --git a/test/build_examples.sh b/test/build_examples.sh index 83c5ed8..8b4b61c 100755 --- a/test/build_examples.sh +++ b/test/build_examples.sh @@ -47,16 +47,22 @@ pwd pwd make -Bs || exit $? make -Bs parallel || exit $? + mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && for f in *.dat; do mmsp2png $f >/dev/null; done + rm test.*.dat if $CLEAN; then make -s clean; fi cd ../phase_field/ pwd make -Bs || exit $? make -Bs parallel || exit $? + mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && for f in *.dat; do mmsp2png $f >/dev/null; done + rm test.*.dat if $CLEAN; then make -s clean; fi cd ../sparsePF/ pwd make -Bs || exit $? make -Bs parallel || exit $? + mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && for f in *.dat; do mmsp2png $f >/dev/null; done + rm test.*.dat if $CLEAN; then make -s clean; fi cd $examples @@ -66,16 +72,22 @@ pwd pwd make -Bs || exit $? make -Bs parallel || exit $? + mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && for f in *.dat; do mmsp2png $f >/dev/null; done + rm test.*.dat if $CLEAN; then make -s clean; fi cd ../phase_field/ pwd make -Bs || exit $? make -Bs parallel || exit $? + mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && for f in *.dat; do mmsp2png $f >/dev/null; done + rm test.*.dat if $CLEAN; then make -s clean; fi cd ../sparsePF/ pwd make -Bs || exit $? make -Bs parallel || exit $? + mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && for f in *.dat; do mmsp2png $f >/dev/null; done + rm test.*.dat if $CLEAN; then make -s clean; fi cd $examples @@ -88,6 +100,8 @@ pwd pwd make -Bs || exit $? make -Bs parallel || exit $? + mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && for f in *.dat; do mmsp2png $f >/dev/null; done + rm test.*.dat if $CLEAN; then make -s clean; fi cd $examples @@ -100,16 +114,22 @@ pwd pwd make -Bs || exit $? make -Bs parallel || exit $? + mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && for f in *.dat; do mmsp2png $f >/dev/null; done + rm test.*.dat if $CLEAN; then make -s clean; fi cd ../phase_field/ pwd make -Bs || exit $? make -Bs parallel || exit $? + mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && for f in *.dat; do mmsp2png $f >/dev/null; done + rm test.*.dat if $CLEAN; then make -s clean; fi cd ../sparsePF/ pwd make -Bs || exit $? make -Bs parallel || exit $? + mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && for f in *.dat; do mmsp2png $f >/dev/null; done + rm test.*.dat if $CLEAN; then make -s clean; fi cd $examples @@ -119,16 +139,22 @@ pwd pwd make -Bs || exit $? make -Bs parallel || exit $? + mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && for f in *.dat; do mmsp2png $f >/dev/null; done + rm test.*.dat if $CLEAN; then make -s clean; fi cd ../phase_field/ pwd make -Bs || exit $? make -Bs parallel || exit $? + mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && for f in *.dat; do mmsp2png $f >/dev/null; done + rm test.*.dat if $CLEAN; then make -s clean; fi cd ../sparsePF/ pwd make -Bs || exit $? make -Bs parallel || exit $? + mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && for f in *.dat; do mmsp2png $f >/dev/null; done + rm test.*.dat if $CLEAN; then make -s clean; fi cd $examples @@ -141,6 +167,8 @@ pwd pwd make -Bs || exit $? make -Bs parallel || exit $? + mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && for f in *.dat; do mmsp2png $f >/dev/null; done + rm test.*.dat if $CLEAN; then make -s clean; fi cd $examples @@ -151,6 +179,8 @@ pwd pwd make -Bs || exit $? make -Bs parallel || exit $? + mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && for f in *.dat; do mmsp2png $f >/dev/null; done + rm test.*.dat if $CLEAN; then make -s clean; fi cd $examples @@ -160,11 +190,15 @@ pwd pwd make -Bs || exit $? make -Bs parallel || exit $? + mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && for f in *.dat; do mmsp2png $f >/dev/null; done + rm test.*.dat if $CLEAN; then make -s clean; fi cd ../explicit/ pwd make -Bs || exit $? make -Bs parallel || exit $? + mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && for f in *.dat; do mmsp2png $f >/dev/null; done + rm test.*.dat if $CLEAN; then make -s clean; fi cd $examples @@ -172,6 +206,8 @@ pwd pwd make -Bs || exit $? make -Bs parallel || exit $? + mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && for f in *.dat; do mmsp2png $f >/dev/null; done + rm test.*.dat if $CLEAN; then make -s clean; fi cd $examples @@ -179,6 +215,8 @@ pwd pwd make -Bs || exit $? make -Bs parallel || exit $? + mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && for f in *.dat; do mmsp2png $f >/dev/null; done + rm test.*.dat if $CLEAN; then make -s clean; fi cd $examples @@ -186,6 +224,8 @@ pwd pwd make -Bs || exit $? make -Bs parallel || exit $? + mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && for f in *.dat; do mmsp2png $f >/dev/null; done + rm test.*.dat if $CLEAN; then make -s clean; fi cd $examples @@ -195,6 +235,8 @@ pwd pwd make -Bs || exit $? make -Bs parallel || exit $? + mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && for f in *.dat; do mmsp2png $f >/dev/null; done + rm test.*.dat if $CLEAN; then make -s clean; fi cd $examples @@ -205,6 +247,8 @@ pwd pwd make -Bs || exit $? make -Bs parallel || exit $? + mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && for f in *.dat; do mmsp2png $f >/dev/null; done + rm test.*.dat if $CLEAN; then make -s clean; fi cd $examples @@ -212,6 +256,8 @@ pwd pwd make -Bs || exit $? make -Bs parallel || exit $? + mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && for f in *.dat; do mmsp2png $f >/dev/null; done + rm test.*.dat if $CLEAN; then make -s clean; fi cd $examples @@ -219,6 +265,8 @@ pwd pwd make -Bs || exit $? make -Bs parallel || exit $? + mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && for f in *.dat; do mmsp2png $f >/dev/null; done + rm test.*.dat if $CLEAN; then make -s clean; fi cd $examples From 38da081f09e19bcc70213c2625ea61308eb589b6 Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Mon, 15 Feb 2016 13:41:01 -0500 Subject: [PATCH 29/83] add counters to tests --- .gitignore | 2 + test/build_examples.sh | 188 ++++++++++++++++++++++++----------------- 2 files changed, 112 insertions(+), 78 deletions(-) diff --git a/.gitignore b/.gitignore index 9d278e3..88aebd2 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,9 @@ *.dvi *.log *.out +*.png *.toc +*.directory doc/MMSP.manual.pdf # MMSP utility binaries diff --git a/test/build_examples.sh b/test/build_examples.sh index 8b4b61c..fa0b774 100755 --- a/test/build_examples.sh +++ b/test/build_examples.sh @@ -1,6 +1,9 @@ #!/bin/bash tstart=$(date +%s) +nSerBld=0 +nParBld=0 +nParRun=0 cd ../examples @@ -32,7 +35,7 @@ examples=$(pwd) cd beginners_diffusion/ pwd - make -Bs || exit $? + (make -Bs || exit $?) && ((nSerBld++)) if $CLEAN; then make -s clean; fi cd $examples @@ -45,23 +48,26 @@ cd anisotropic pwd cd Monte_Carlo pwd - make -Bs || exit $? - make -Bs parallel || exit $? - mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && for f in *.dat; do mmsp2png $f >/dev/null; done + (make -Bs || exit $?) && ((nSerBld++)) + (make -Bs parallel || exit $?) && ((nParBld++)) + mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && ((nParRun++)) + for f in *.dat; do mmsp2png --zoom $f >/dev/null; done rm test.*.dat if $CLEAN; then make -s clean; fi cd ../phase_field/ pwd - make -Bs || exit $? - make -Bs parallel || exit $? - mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && for f in *.dat; do mmsp2png $f >/dev/null; done + (make -Bs || exit $?) && ((nSerBld++)) + (make -Bs parallel || exit $?) && ((nParBld++)) + mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && ((nParRun++)) + for f in *.dat; do mmsp2png --zoom $f >/dev/null; done rm test.*.dat if $CLEAN; then make -s clean; fi cd ../sparsePF/ pwd - make -Bs || exit $? - make -Bs parallel || exit $? - mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && for f in *.dat; do mmsp2png $f >/dev/null; done + (make -Bs || exit $?) && ((nSerBld++)) + (make -Bs parallel || exit $?) && ((nParBld++)) + mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && ((nParRun++)) + for f in *.dat; do mmsp2png --zoom $f >/dev/null; done rm test.*.dat if $CLEAN; then make -s clean; fi cd $examples @@ -70,23 +76,26 @@ cd coarsening/grain_growth/isotropic pwd cd Monte_Carlo/ pwd - make -Bs || exit $? - make -Bs parallel || exit $? - mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && for f in *.dat; do mmsp2png $f >/dev/null; done + (make -Bs || exit $?) && ((nSerBld++)) + (make -Bs parallel || exit $?) && ((nParBld++)) + mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && ((nParRun++)) + for f in *.dat; do mmsp2png --zoom $f >/dev/null; done rm test.*.dat if $CLEAN; then make -s clean; fi cd ../phase_field/ pwd - make -Bs || exit $? - make -Bs parallel || exit $? - mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && for f in *.dat; do mmsp2png $f >/dev/null; done + (make -Bs || exit $?) && ((nSerBld++)) + (make -Bs parallel || exit $?) && ((nParBld++)) + mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && ((nParRun++)) + for f in *.dat; do mmsp2png --zoom $f >/dev/null; done rm test.*.dat if $CLEAN; then make -s clean; fi cd ../sparsePF/ pwd - make -Bs || exit $? - make -Bs parallel || exit $? - mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && for f in *.dat; do mmsp2png $f >/dev/null; done + (make -Bs || exit $?) && ((nSerBld++)) + (make -Bs parallel || exit $?) && ((nParBld++)) + mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && ((nParRun++)) + for f in *.dat; do mmsp2png --zoom $f >/dev/null; done rm test.*.dat if $CLEAN; then make -s clean; fi cd $examples @@ -98,9 +107,10 @@ cd isotropic pwd cd phase_field/ pwd - make -Bs || exit $? - make -Bs parallel || exit $? - mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && for f in *.dat; do mmsp2png $f >/dev/null; done + (make -Bs || exit $?) && ((nSerBld++)) + (make -Bs parallel || exit $?) && ((nParBld++)) + mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && ((nParRun++)) + for f in *.dat; do mmsp2png --zoom $f >/dev/null; done rm test.*.dat if $CLEAN; then make -s clean; fi cd $examples @@ -112,23 +122,26 @@ cd anisotropic pwd cd Monte_Carlo/ pwd - make -Bs || exit $? - make -Bs parallel || exit $? - mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && for f in *.dat; do mmsp2png $f >/dev/null; done + (make -Bs || exit $?) && ((nSerBld++)) + (make -Bs parallel || exit $?) && ((nParBld++)) + mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && ((nParRun++)) + for f in *.dat; do mmsp2png --zoom $f >/dev/null; done rm test.*.dat if $CLEAN; then make -s clean; fi cd ../phase_field/ pwd - make -Bs || exit $? - make -Bs parallel || exit $? - mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && for f in *.dat; do mmsp2png $f >/dev/null; done + (make -Bs || exit $?) && ((nSerBld++)) + (make -Bs parallel || exit $?) && ((nParBld++)) + mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && ((nParRun++)) + for f in *.dat; do mmsp2png --zoom $f >/dev/null; done rm test.*.dat if $CLEAN; then make -s clean; fi cd ../sparsePF/ pwd - make -Bs || exit $? - make -Bs parallel || exit $? - mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && for f in *.dat; do mmsp2png $f >/dev/null; done + (make -Bs || exit $?) && ((nSerBld++)) + (make -Bs parallel || exit $?) && ((nParBld++)) + mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && ((nParRun++)) + for f in *.dat; do mmsp2png --zoom $f >/dev/null; done rm test.*.dat if $CLEAN; then make -s clean; fi cd $examples @@ -137,23 +150,26 @@ cd coarsening/zener_pinning/isotropic pwd cd Monte_Carlo/ pwd - make -Bs || exit $? - make -Bs parallel || exit $? - mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && for f in *.dat; do mmsp2png $f >/dev/null; done + (make -Bs || exit $?) && ((nSerBld++)) + (make -Bs parallel || exit $?) && ((nParBld++)) + mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && ((nParRun++)) + for f in *.dat; do mmsp2png --zoom $f >/dev/null; done rm test.*.dat if $CLEAN; then make -s clean; fi cd ../phase_field/ pwd - make -Bs || exit $? - make -Bs parallel || exit $? - mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && for f in *.dat; do mmsp2png $f >/dev/null; done + (make -Bs || exit $?) && ((nSerBld++)) + (make -Bs parallel || exit $?) && ((nParBld++)) + mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && ((nParRun++)) + for f in *.dat; do mmsp2png --zoom $f >/dev/null; done rm test.*.dat if $CLEAN; then make -s clean; fi cd ../sparsePF/ pwd - make -Bs || exit $? - make -Bs parallel || exit $? - mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && for f in *.dat; do mmsp2png $f >/dev/null; done + (make -Bs || exit $?) && ((nSerBld++)) + (make -Bs parallel || exit $?) && ((nParBld++)) + mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && ((nParRun++)) + for f in *.dat; do mmsp2png --zoom $f >/dev/null; done rm test.*.dat if $CLEAN; then make -s clean; fi cd $examples @@ -162,12 +178,12 @@ echo cd differential_equations pwd cd elliptic -pwd cd Poisson/ pwd - make -Bs || exit $? - make -Bs parallel || exit $? - mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && for f in *.dat; do mmsp2png $f >/dev/null; done + (make -Bs || exit $?) && ((nSerBld++)) + (make -Bs parallel || exit $?) && ((nParBld++)) + mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && ((nParRun++)) + for f in *.dat; do mmsp2png --zoom $f >/dev/null; done rm test.*.dat if $CLEAN; then make -s clean; fi cd $examples @@ -177,54 +193,62 @@ cd phase_transitions pwd cd allen-cahn/ pwd - make -Bs || exit $? - make -Bs parallel || exit $? - mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && for f in *.dat; do mmsp2png $f >/dev/null; done + (make -Bs || exit $?) && ((nSerBld++)) + (make -Bs parallel || exit $?) && ((nParBld++)) + mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && ((nParRun++)) + for f in *.dat; do mmsp2png --zoom $f >/dev/null; done rm test.*.dat if $CLEAN; then make -s clean; fi cd $examples cd phase_transitions/cahn-hilliard pwd - cd convex_splitting/ + echo "Skipped phase_transitions/cahn-hilliard/convex_splitting/" +# convex splitting is not a suitable example -- it runs much, much too long! + cd convex_splitting/ pwd - make -Bs || exit $? - make -Bs parallel || exit $? - mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && for f in *.dat; do mmsp2png $f >/dev/null; done - rm test.*.dat + (make -Bs || exit $?) && ((nSerBld++)) + (make -Bs parallel || exit $?) && ((nParBld++)) +# mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && ((nParRun++)) +# for f in *.dat; do mmsp2png --zoom $f >/dev/null; done +# rm test.*.dat if $CLEAN; then make -s clean; fi cd ../explicit/ pwd - make -Bs || exit $? - make -Bs parallel || exit $? - mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && for f in *.dat; do mmsp2png $f >/dev/null; done + (make -Bs || exit $?) && ((nSerBld++)) + (make -Bs parallel || exit $?) && ((nParBld++)) + mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && ((nParRun++)) + for f in *.dat; do mmsp2png --zoom $f >/dev/null; done rm test.*.dat if $CLEAN; then make -s clean; fi cd $examples cd phase_transitions/model_A pwd - make -Bs || exit $? - make -Bs parallel || exit $? - mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && for f in *.dat; do mmsp2png $f >/dev/null; done + (make -Bs || exit $?) && ((nSerBld++)) + (make -Bs parallel || exit $?) && ((nParBld++)) + mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && ((nParRun++)) + for f in *.dat; do mmsp2png --zoom $f >/dev/null; done rm test.*.dat if $CLEAN; then make -s clean; fi cd $examples cd phase_transitions/model_B/ pwd - make -Bs || exit $? - make -Bs parallel || exit $? - mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && for f in *.dat; do mmsp2png $f >/dev/null; done + (make -Bs || exit $?) && ((nSerBld++)) + (make -Bs parallel || exit $?) && ((nParBld++)) + mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && ((nParRun++)) + for f in *.dat; do mmsp2png --zoom $f >/dev/null; done rm test.*.dat if $CLEAN; then make -s clean; fi cd $examples cd phase_transitions/spinodal/ pwd - make -Bs || exit $? - make -Bs parallel || exit $? - mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && for f in *.dat; do mmsp2png $f >/dev/null; done + (make -Bs || exit $?) && ((nSerBld++)) + (make -Bs parallel || exit $?) && ((nParBld++)) + mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && ((nParRun++)) + for f in *.dat; do mmsp2png --zoom $f >/dev/null; done rm test.*.dat if $CLEAN; then make -s clean; fi cd $examples @@ -233,9 +257,10 @@ cd phase_transitions/solidification pwd cd anisotropic/ pwd - make -Bs || exit $? - make -Bs parallel || exit $? - mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && for f in *.dat; do mmsp2png $f >/dev/null; done + (make -Bs || exit $?) && ((nSerBld++)) + (make -Bs parallel || exit $?) && ((nParBld++)) + mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && ((nParRun++)) + for f in *.dat; do mmsp2png --zoom $f >/dev/null; done rm test.*.dat if $CLEAN; then make -s clean; fi cd $examples @@ -245,35 +270,42 @@ cd statistical_mechanics pwd cd Heisenberg/ pwd - make -Bs || exit $? - make -Bs parallel || exit $? - mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && for f in *.dat; do mmsp2png $f >/dev/null; done + (make -Bs || exit $?) && ((nSerBld++)) + (make -Bs parallel || exit $?) && ((nParBld++)) + mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && ((nParRun++)) + for f in *.dat; do mmsp2png --zoom $f >/dev/null; done rm test.*.dat if $CLEAN; then make -s clean; fi cd $examples cd statistical_mechanics/Ising/ pwd - make -Bs || exit $? - make -Bs parallel || exit $? - mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && for f in *.dat; do mmsp2png $f >/dev/null; done + (make -Bs || exit $?) && ((nSerBld++)) + (make -Bs parallel || exit $?) && ((nParBld++)) + mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && ((nParRun++)) + for f in *.dat; do mmsp2png --zoom $f >/dev/null; done rm test.*.dat if $CLEAN; then make -s clean; fi cd $examples cd statistical_mechanics/Potts/ pwd - make -Bs || exit $? - make -Bs parallel || exit $? - mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && for f in *.dat; do mmsp2png $f >/dev/null; done + (make -Bs || exit $?) && ((nSerBld++)) + (make -Bs parallel || exit $?) && ((nParBld++)) + mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && ((nParRun++)) + for f in *.dat; do mmsp2png --zoom $f >/dev/null; done rm test.*.dat if $CLEAN; then make -s clean; fi cd $examples +echo +echo "${nSerBld} serial examples built successfully." +echo "${nParBld} parallel examples built successfully." +echo "${nParRun} parallel examples executed successfully." + tfinish=$(date +%s) elapsed=$(echo "$tfinish-$tstart" | bc -l) -echo -echo "Examples built successfully. ${elapsed} seconds elapsed." +echo "${elapsed} seconds elapsed." cd ../test/ From 658779d1b1c7dc7eed315517839b565f2aa07b64 Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Mon, 15 Feb 2016 16:48:14 -0500 Subject: [PATCH 30/83] streamline build script --- test/build_examples.sh | 314 ++++++++--------------------------------- 1 file changed, 55 insertions(+), 259 deletions(-) diff --git a/test/build_examples.sh b/test/build_examples.sh index fa0b774..10c94a7 100755 --- a/test/build_examples.sh +++ b/test/build_examples.sh @@ -4,6 +4,7 @@ tstart=$(date +%s) nSerBld=0 nParBld=0 nParRun=0 +iters=1000 cd ../examples @@ -15,6 +16,10 @@ do CLEAN=true shift # past argument ;; + -l|--long) + iters=5000 + shift # past argument + ;; *) # unknown option echo "WARNING: Unknown option ${key}." @@ -24,7 +29,8 @@ do shift # past argument or value done -if [[ $CLEAN ]]; then +if [[ $CLEAN ]] +then echo "Building examples in serial and parallel, then deleting binaries." else echo "Building examples in serial and parallel, leaving binaries behind (specify --clean to clean up)." @@ -33,270 +39,60 @@ echo examples=$(pwd) -cd beginners_diffusion/ -pwd - (make -Bs || exit $?) && ((nSerBld++)) - if $CLEAN; then make -s clean; fi - cd $examples - -echo -cd coarsening -pwd -cd grain_growth -pwd -cd anisotropic -pwd - cd Monte_Carlo - pwd - (make -Bs || exit $?) && ((nSerBld++)) - (make -Bs parallel || exit $?) && ((nParBld++)) - mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && ((nParRun++)) - for f in *.dat; do mmsp2png --zoom $f >/dev/null; done - rm test.*.dat - if $CLEAN; then make -s clean; fi - cd ../phase_field/ - pwd - (make -Bs || exit $?) && ((nSerBld++)) - (make -Bs parallel || exit $?) && ((nParBld++)) - mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && ((nParRun++)) - for f in *.dat; do mmsp2png --zoom $f >/dev/null; done - rm test.*.dat - if $CLEAN; then make -s clean; fi - cd ../sparsePF/ - pwd - (make -Bs || exit $?) && ((nSerBld++)) - (make -Bs parallel || exit $?) && ((nParBld++)) - mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && ((nParRun++)) - for f in *.dat; do mmsp2png --zoom $f >/dev/null; done - rm test.*.dat - if $CLEAN; then make -s clean; fi - cd $examples - -cd coarsening/grain_growth/isotropic -pwd - cd Monte_Carlo/ - pwd - (make -Bs || exit $?) && ((nSerBld++)) - (make -Bs parallel || exit $?) && ((nParBld++)) - mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && ((nParRun++)) - for f in *.dat; do mmsp2png --zoom $f >/dev/null; done - rm test.*.dat - if $CLEAN; then make -s clean; fi - cd ../phase_field/ - pwd - (make -Bs || exit $?) && ((nSerBld++)) - (make -Bs parallel || exit $?) && ((nParBld++)) - mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && ((nParRun++)) - for f in *.dat; do mmsp2png --zoom $f >/dev/null; done - rm test.*.dat - if $CLEAN; then make -s clean; fi - cd ../sparsePF/ - pwd - (make -Bs || exit $?) && ((nSerBld++)) - (make -Bs parallel || exit $?) && ((nParBld++)) - mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && ((nParRun++)) - for f in *.dat; do mmsp2png --zoom $f >/dev/null; done - rm test.*.dat - if $CLEAN; then make -s clean; fi - cd $examples - -echo -cd coarsening/ostwald_ripening -pwd -cd isotropic -pwd - cd phase_field/ - pwd - (make -Bs || exit $?) && ((nSerBld++)) - (make -Bs parallel || exit $?) && ((nParBld++)) - mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && ((nParRun++)) - for f in *.dat; do mmsp2png --zoom $f >/dev/null; done - rm test.*.dat - if $CLEAN; then make -s clean; fi - cd $examples - -echo -cd coarsening/zener_pinning -pwd -cd anisotropic -pwd - cd Monte_Carlo/ - pwd - (make -Bs || exit $?) && ((nSerBld++)) - (make -Bs parallel || exit $?) && ((nParBld++)) - mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && ((nParRun++)) - for f in *.dat; do mmsp2png --zoom $f >/dev/null; done - rm test.*.dat - if $CLEAN; then make -s clean; fi - cd ../phase_field/ - pwd - (make -Bs || exit $?) && ((nSerBld++)) - (make -Bs parallel || exit $?) && ((nParBld++)) - mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && ((nParRun++)) - for f in *.dat; do mmsp2png --zoom $f >/dev/null; done - rm test.*.dat - if $CLEAN; then make -s clean; fi - cd ../sparsePF/ - pwd - (make -Bs || exit $?) && ((nSerBld++)) - (make -Bs parallel || exit $?) && ((nParBld++)) - mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && ((nParRun++)) - for f in *.dat; do mmsp2png --zoom $f >/dev/null; done - rm test.*.dat - if $CLEAN; then make -s clean; fi - cd $examples - -cd coarsening/zener_pinning/isotropic -pwd - cd Monte_Carlo/ - pwd - (make -Bs || exit $?) && ((nSerBld++)) - (make -Bs parallel || exit $?) && ((nParBld++)) - mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && ((nParRun++)) - for f in *.dat; do mmsp2png --zoom $f >/dev/null; done - rm test.*.dat - if $CLEAN; then make -s clean; fi - cd ../phase_field/ - pwd - (make -Bs || exit $?) && ((nSerBld++)) - (make -Bs parallel || exit $?) && ((nParBld++)) - mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && ((nParRun++)) - for f in *.dat; do mmsp2png --zoom $f >/dev/null; done - rm test.*.dat - if $CLEAN; then make -s clean; fi - cd ../sparsePF/ - pwd - (make -Bs || exit $?) && ((nSerBld++)) - (make -Bs parallel || exit $?) && ((nParBld++)) - mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && ((nParRun++)) - for f in *.dat; do mmsp2png --zoom $f >/dev/null; done - rm test.*.dat - if $CLEAN; then make -s clean; fi - cd $examples - -echo -cd differential_equations -pwd -cd elliptic - cd Poisson/ - pwd - (make -Bs || exit $?) && ((nSerBld++)) - (make -Bs parallel || exit $?) && ((nParBld++)) - mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && ((nParRun++)) - for f in *.dat; do mmsp2png --zoom $f >/dev/null; done - rm test.*.dat - if $CLEAN; then make -s clean; fi - cd $examples - -echo -cd phase_transitions -pwd - cd allen-cahn/ - pwd - (make -Bs || exit $?) && ((nSerBld++)) - (make -Bs parallel || exit $?) && ((nParBld++)) - mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && ((nParRun++)) - for f in *.dat; do mmsp2png --zoom $f >/dev/null; done - rm test.*.dat - if $CLEAN; then make -s clean; fi - cd $examples - - cd phase_transitions/cahn-hilliard - pwd - echo "Skipped phase_transitions/cahn-hilliard/convex_splitting/" -# convex splitting is not a suitable example -- it runs much, much too long! - cd convex_splitting/ - pwd - (make -Bs || exit $?) && ((nSerBld++)) - (make -Bs parallel || exit $?) && ((nParBld++)) -# mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && ((nParRun++)) -# for f in *.dat; do mmsp2png --zoom $f >/dev/null; done -# rm test.*.dat - if $CLEAN; then make -s clean; fi - cd ../explicit/ - pwd - (make -Bs || exit $?) && ((nSerBld++)) - (make -Bs parallel || exit $?) && ((nParBld++)) - mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && ((nParRun++)) - for f in *.dat; do mmsp2png --zoom $f >/dev/null; done - rm test.*.dat - if $CLEAN; then make -s clean; fi - cd $examples - - cd phase_transitions/model_A - pwd - (make -Bs || exit $?) && ((nSerBld++)) - (make -Bs parallel || exit $?) && ((nParBld++)) - mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && ((nParRun++)) - for f in *.dat; do mmsp2png --zoom $f >/dev/null; done - rm test.*.dat - if $CLEAN; then make -s clean; fi - cd $examples +exdirs="coarsening/grain_growth/anisotropic/Monte_Carlo/ +coarsening/grain_growth/anisotropic/phase_field/ +coarsening/grain_growth/anisotropic/sparsePF/ +coarsening/grain_growth/isotropic/Monte_Carlo/ +coarsening/grain_growth/isotropic/phase_field/ +coarsening/grain_growth/isotropic/sparsePF/ +coarsening/ostwald_ripening/isotropic/phase_field/ +coarsening/zener_pinning/anisotropic/Monte_Carlo/ +coarsening/zener_pinning/anisotropic/phase_field/ +coarsening/zener_pinning/anisotropic/sparsePF/ +coarsening/zener_pinning/isotropic/Monte_Carlo/ +coarsening/zener_pinning/isotropic/phase_field/ +coarsening/zener_pinning/isotropic/sparsePF/ +phase_transitions/allen-cahn/ +phase_transitions/cahn-hilliard/explicit/ +phase_transitions/model_A/ +phase_transitions/model_B/ +phase_transitions/solidification/anisotropic/ +phase_transitions/spinodal/ +statistical_mechanics/Heisenberg/ +statistical_mechanics/Ising/ +statistical_mechanics/Potts/" + +# Skip phase_transitions/cahn-hilliard/convex_splitting -- too long +# Skip differential_equations/elliptic/Poisson -- segmentation faults +# Skip beginners_diffusion -- does not match execution pattern + +for d in $exdirs +do + cd ${examples}/$d + printf "%-55s\t" $d + exstart=$(date +%s) - cd phase_transitions/model_B/ - pwd (make -Bs || exit $?) && ((nSerBld++)) (make -Bs parallel || exit $?) && ((nParBld++)) - mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && ((nParRun++)) - for f in *.dat; do mmsp2png --zoom $f >/dev/null; done - rm test.*.dat - if $CLEAN; then make -s clean; fi - cd $examples + mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat $iters 500 >/dev/null && ((nParRun++)) - cd phase_transitions/spinodal/ - pwd - (make -Bs || exit $?) && ((nSerBld++)) - (make -Bs parallel || exit $?) && ((nParBld++)) - mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && ((nParRun++)) - for f in *.dat; do mmsp2png --zoom $f >/dev/null; done - rm test.*.dat - if $CLEAN; then make -s clean; fi - cd $examples + for f in *.dat + do + mmsp2png --zoom $f >/dev/null + done -cd phase_transitions/solidification -pwd - cd anisotropic/ - pwd - (make -Bs || exit $?) && ((nSerBld++)) - (make -Bs parallel || exit $?) && ((nParBld++)) - mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && ((nParRun++)) - for f in *.dat; do mmsp2png --zoom $f >/dev/null; done - rm test.*.dat - if $CLEAN; then make -s clean; fi - cd $examples + if [[ $CLEAN ]] + then + rm test.*.dat + rm test.*.png + make -s clean + fi -echo -cd statistical_mechanics -pwd - cd Heisenberg/ - pwd - (make -Bs || exit $?) && ((nSerBld++)) - (make -Bs parallel || exit $?) && ((nParBld++)) - mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && ((nParRun++)) - for f in *.dat; do mmsp2png --zoom $f >/dev/null; done - rm test.*.dat - if $CLEAN; then make -s clean; fi - cd $examples - - cd statistical_mechanics/Ising/ - pwd - (make -Bs || exit $?) && ((nSerBld++)) - (make -Bs parallel || exit $?) && ((nParBld++)) - mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && ((nParRun++)) - for f in *.dat; do mmsp2png --zoom $f >/dev/null; done - rm test.*.dat - if $CLEAN; then make -s clean; fi - cd $examples - - cd statistical_mechanics/Potts/ - pwd - (make -Bs || exit $?) && ((nSerBld++)) - (make -Bs parallel || exit $?) && ((nParBld++)) - mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat 1000 500 >/dev/null && ((nParRun++)) - for f in *.dat; do mmsp2png --zoom $f >/dev/null; done - rm test.*.dat - if $CLEAN; then make -s clean; fi - cd $examples + exfin=$(date +%s) + exlapse=$(echo "$exfin-$exstart" | bc -l) + echo "${exlapse} seconds" +done +cd ${examples} echo echo "${nSerBld} serial examples built successfully." From 2e8f2f087bd105c0ad0ec985acae8d1baba1a9e9 Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Mon, 15 Feb 2016 19:15:06 -0500 Subject: [PATCH 31/83] remove existing files before writing, resize examples --- .../anisotropic/Monte_Carlo/graingrowth.cpp | 4 ++-- .../anisotropic/phase_field/graingrowth.cpp | 13 +++++++------ .../anisotropic/sparsePF/graingrowth.cpp | 13 +++++++------ .../isotropic/Monte_Carlo/graingrowth.cpp | 5 +++-- .../isotropic/phase_field/graingrowth.cpp | 7 ++++--- .../isotropic/sparsePF/graingrowth.cpp | 7 ++++--- .../isotropic/phase_field/ostwald.cpp | 2 +- .../anisotropic/Monte_Carlo/zener.cpp | 7 ++++--- .../anisotropic/phase_field/zener.cpp | 7 ++++--- .../anisotropic/sparsePF/zener.cpp | 7 ++++--- .../isotropic/Monte_Carlo/zener.cpp | 7 ++++--- .../isotropic/phase_field/zener.cpp | 7 ++++--- .../zener_pinning/isotropic/sparsePF/zener.cpp | 7 ++++--- .../elliptic/Poisson/poisson.cpp | 16 +++++++++------- .../allen-cahn/allen-cahn.cpp | 6 +++--- .../cahn-hilliard/explicit/cahn-hilliard.cpp | 6 +++--- examples/phase_transitions/model_A/model_A.cpp | 6 +++--- examples/phase_transitions/model_B/model_B.cpp | 6 +++--- .../solidification/anisotropic/Makefile | 4 ++-- .../anisotropic/solidification.cpp | 6 +++--- .../phase_transitions/spinodal/spinodal.cpp | 6 +++--- .../Heisenberg/heisenberg.cpp | 2 +- examples/statistical_mechanics/Ising/ising.cpp | 6 +++--- examples/statistical_mechanics/Potts/potts.cpp | 6 +++--- include/MMSP.grid.hpp | 18 ++++++++++++------ 25 files changed, 100 insertions(+), 81 deletions(-) diff --git a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp index 49e09db..3e5a9aa 100644 --- a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp +++ b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp @@ -33,14 +33,14 @@ void generate(int dim, const char* filename) } if (dim == 2) { - GRID2D initGrid(2, 0, 64, 0, 64); + GRID2D initGrid(0, 0, 256, 0, 256); for (int i = 0; i < nodes(initGrid); i++) initGrid(i) = rand() % 20; output(initGrid, filename); } else if (dim == 3) { - GRID3D initGrid(2, 0, 32, 0, 32, 0, 32); + GRID3D initGrid(0, 0, 32, 0, 32, 0, 32); for (int i = 0; i < nodes(initGrid); i++) initGrid(i) = rand() % 20; diff --git a/examples/coarsening/grain_growth/anisotropic/phase_field/graingrowth.cpp b/examples/coarsening/grain_growth/anisotropic/phase_field/graingrowth.cpp index 4c62edc..125e96b 100644 --- a/examples/coarsening/grain_growth/anisotropic/phase_field/graingrowth.cpp +++ b/examples/coarsening/grain_growth/anisotropic/phase_field/graingrowth.cpp @@ -31,7 +31,8 @@ void generate(int dim, const char* filename) } if (dim==2) { - GRID2D initGrid(4,0,128,0,128); + int L=128; + GRID2D initGrid(4,0,L,0,L); for (int i=0; i x = position(initGrid,i); - if (x[0]<32) { - if (x[1]<64) initGrid(i)[2] = 1.0; + if (x[0]96) { - if (x[1]<64) initGrid(i)[2] = 1.0; + else if (x[0]>3*L/4) { + if (x[1]96) initGrid(i)[1] = 1.0; + if (x[1]3*L/4) initGrid(i)[1] = 1.0; else initGrid(i)[0] = 1.0; } } diff --git a/examples/coarsening/grain_growth/anisotropic/sparsePF/graingrowth.cpp b/examples/coarsening/grain_growth/anisotropic/sparsePF/graingrowth.cpp index f39d16c..7021915 100644 --- a/examples/coarsening/grain_growth/anisotropic/sparsePF/graingrowth.cpp +++ b/examples/coarsening/grain_growth/anisotropic/sparsePF/graingrowth.cpp @@ -28,21 +28,22 @@ void generate(int dim, const char* filename) } if (dim==2) { - GRID2D initGrid(0,0,128,0,128); + int L=128; + GRID2D initGrid(0,0,L,0,L); for (int i=0; i x = position(initGrid,i); - if (x[0]<32) { - if (x[1]<64) set(initGrid(i),2) = 1.0; + if (x[0]96) { - if (x[1]<64) set(initGrid(i),2) = 1.0; + else if (x[0]>3*L/4) { + if (x[1]96) set(initGrid(i),1) = 1.0; + if (x[1]3*L/4) set(initGrid(i),1) = 1.0; else set(initGrid(i),0) = 1.0; } } diff --git a/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp b/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp index 73c9f04..a2f7454 100644 --- a/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp +++ b/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp @@ -28,13 +28,14 @@ void generate(int dim, const char* filename) } if (dim == 2) { - GRID2D initGrid(2, 0, 64, 0, 64); + int L=256; + GRID2D initGrid(0, 0, L, 0, L); for (int i = 0; i < nodes(initGrid); i++) initGrid(i) = rand() % 20; output(initGrid, filename); } else if (dim == 3) { - GRID3D initGrid(2, 0, 32, 0, 32, 0, 32); + GRID3D initGrid(0, 0, 32, 0, 32, 0, 32); for (int i = 0; i < nodes(initGrid); i++) initGrid(i) = rand() % 20; diff --git a/examples/coarsening/grain_growth/isotropic/phase_field/graingrowth.cpp b/examples/coarsening/grain_growth/isotropic/phase_field/graingrowth.cpp index 03cf03d..450605b 100644 --- a/examples/coarsening/grain_growth/isotropic/phase_field/graingrowth.cpp +++ b/examples/coarsening/grain_growth/isotropic/phase_field/graingrowth.cpp @@ -31,12 +31,13 @@ void generate(int dim, const char* filename) } if (dim==2) { - GRID2D initGrid(2,0,128,0,128); + int L=128; + GRID2D initGrid(2,0,L,0,L); for (int i=0; i x = position(initGrid,i); - double d = sqrt(pow(64.0-x[0],2)+pow(64.0-x[1],2)); - if (d<32.0) { + double d = sqrt(pow(L/2-x[0],2)+pow(L/2-x[1],2)); + if (d x = position(initGrid,i); - double d = sqrt(pow(64.0-x[0],2)+pow(64.0-x[1],2)); - if (d<32.0) set(initGrid(i),1)= 1.0; + double d = sqrt(pow(L/2-x[0],2)+pow(L/2-x[1],2)); + if (d x = position(initGrid,i); - double d = sqrt(pow(64.0-x[0],2)+pow(64.0-x[1],2)); - if (d<32.0) initGrid(i) = 2; + double d = sqrt(pow(L/2-x[0],2)+pow(L/2-x[1],2)); + if (d x = position(initGrid,i); initGrid(i)[0] = 0.0; initGrid(i)[1] = 0.0; - double d = sqrt(pow(64.0-x[0],2)+pow(64.0-x[1],2)); - if (d<32.0) initGrid(i)[2] = 1.0; + double d = sqrt(pow(L/2.0-x[0],2)+pow(L/2.0-x[1],2)); + if (d x = position(initGrid,i); - double d = sqrt(pow(64.0-x[0],2)+pow(64.0-x[1],2)); - if (d<32.0) set(initGrid(i),2) = 1.0; + double d = sqrt(pow(L/2-x[0],2)+pow(L/2-x[1],2)); + if (d x = position(grid,i); - double d = sqrt(pow(64.0-x[0],2)+pow(64.0-x[1],2)); - if (d<32.0) grid(i) = 2; + double d = sqrt(pow(L/2-x[0],2)+pow(L/2-x[1],2)); + if (d x = position(initGrid,i); initGrid(i)[0] = 0.0; initGrid(i)[1] = 0.0; - double d = sqrt(pow(64.0-x[0],2)+pow(64.0-x[1],2)); - if (d<32.0) initGrid(i)[2] = 1.0; + double d = sqrt(pow(L/2-x[0],2)+pow(L/2-x[1],2)); + if (d x = position(initGrid,i); - double d = sqrt(pow(64.0-x[0],2)+pow(64.0-x[1],2)); - if (d<32.0) set(initGrid(i),2) = 1.0; + double d = sqrt(pow(L/2-x[0],2)+pow(L/2-x[1],2)); + if (d initGrid(1,0,128,0,128); + int L=256; + grid<2,double> initGrid(1,0,L,0,L); for (int n=0; n x = position(initGrid, n); - double X = double(x[0])/128.0; - double Y = double(x[1])/128.0; + double X = double(x[0])/L; + double Y = double(x[1])/L; initGrid(n) = exp(X*Y); } @@ -216,13 +217,14 @@ void generate(int dim, const char* filename) } if (dim==3) { - grid<3,double> initGrid(1,0,64,0,64,0,64); + int L=64; + grid<3,double> initGrid(1,0,L,0,L,0,L); for (int n=0; n x = position(initGrid, n); - double X = double(x[0])/128.0; - double Y = double(x[1])/128.0; - double Z = double(x[2])/128.0; + double X = double(x[0])/L; + double Y = double(x[1])/L; + double Z = double(x[2])/L; initGrid(n) = exp(X*Y*Z); } diff --git a/examples/phase_transitions/allen-cahn/allen-cahn.cpp b/examples/phase_transitions/allen-cahn/allen-cahn.cpp index bec1dcf..1ae0a99 100644 --- a/examples/phase_transitions/allen-cahn/allen-cahn.cpp +++ b/examples/phase_transitions/allen-cahn/allen-cahn.cpp @@ -14,7 +14,7 @@ void generate(int dim, const char* filename) { // srand() is called exactly once in MMSP.main.hpp. Do not call it here. if (dim==1) { - GRID1D initGrid(1,0,128); + GRID1D initGrid(0,0,128); for (int i=0; i x = position(initGrid,i); - int r=sqrt(pow(x[0]-edge/2,2)+pow(x[1]-edge/2,2)); + int r=sqrt(pow(x[0]-L/2,2)+pow(x[1]-L/2,2)); if (r<=R) initGrid(i)[0]=1.; else initGrid(i)[0]=0.; } diff --git a/examples/phase_transitions/spinodal/spinodal.cpp b/examples/phase_transitions/spinodal/spinodal.cpp index cb0c10e..8f95034 100644 --- a/examples/phase_transitions/spinodal/spinodal.cpp +++ b/examples/phase_transitions/spinodal/spinodal.cpp @@ -41,7 +41,7 @@ double gaussian(double ave, double std) void generate(int dim, const char* filename) { if (dim==1) { - GRID1D initGrid(1,0,128); + GRID1D initGrid(0,0,128); for (int i=0; i Date: Tue, 16 Feb 2016 01:27:27 -0500 Subject: [PATCH 32/83] add extra long option to build script. Monte Carlo examples may suffer deadlock, np-dependent --- test/build_examples.sh | 112 ++++++++++++++++++++++++----------------- 1 file changed, 66 insertions(+), 46 deletions(-) diff --git a/test/build_examples.sh b/test/build_examples.sh index 10c94a7..9106f93 100755 --- a/test/build_examples.sh +++ b/test/build_examples.sh @@ -4,21 +4,43 @@ tstart=$(date +%s) nSerBld=0 nParBld=0 nParRun=0 -iters=1000 +ITERS=1000 +INTER=500 +CORES=4 +EXEC=true cd ../examples +examples=$(pwd) + +echo -n "Building examples in serial and parallel" + while [[ $# > 0 ]] do key="$1" case $key in -c|--clean) + echo -n ", cleaning up after" + CLEAN=true + ;; + -p|--purge) + echo -n ", cleaning up after" CLEAN=true - shift # past argument + PURGE=true + ;; + -n|--noexec) + echo -n ", not executing" + EXEC=false ;; -l|--long) - iters=5000 - shift # past argument + echo -n ", taking 5,000 steps" + ITERS=5000 + INTER=1000 + ;; + -x|--extra) + echo -n ", taking 25,000 steps" + ITERS=25000 + INTER=5000 ;; *) # unknown option @@ -29,63 +51,61 @@ do shift # past argument or value done -if [[ $CLEAN ]] -then - echo "Building examples in serial and parallel, then deleting binaries." -else - echo "Building examples in serial and parallel, leaving binaries behind (specify --clean to clean up)." -fi echo -examples=$(pwd) - -exdirs="coarsening/grain_growth/anisotropic/Monte_Carlo/ -coarsening/grain_growth/anisotropic/phase_field/ -coarsening/grain_growth/anisotropic/sparsePF/ -coarsening/grain_growth/isotropic/Monte_Carlo/ -coarsening/grain_growth/isotropic/phase_field/ -coarsening/grain_growth/isotropic/sparsePF/ -coarsening/ostwald_ripening/isotropic/phase_field/ -coarsening/zener_pinning/anisotropic/Monte_Carlo/ -coarsening/zener_pinning/anisotropic/phase_field/ -coarsening/zener_pinning/anisotropic/sparsePF/ -coarsening/zener_pinning/isotropic/Monte_Carlo/ -coarsening/zener_pinning/isotropic/phase_field/ -coarsening/zener_pinning/isotropic/sparsePF/ -phase_transitions/allen-cahn/ -phase_transitions/cahn-hilliard/explicit/ -phase_transitions/model_A/ -phase_transitions/model_B/ -phase_transitions/solidification/anisotropic/ -phase_transitions/spinodal/ -statistical_mechanics/Heisenberg/ -statistical_mechanics/Ising/ -statistical_mechanics/Potts/" +exdirs=("coarsening/grain_growth/anisotropic/Monte_Carlo/" \ +"coarsening/grain_growth/anisotropic/phase_field/" \ +"coarsening/grain_growth/anisotropic/sparsePF/" \ +"coarsening/grain_growth/isotropic/Monte_Carlo/" \ +"coarsening/grain_growth/isotropic/phase_field/" \ +"coarsening/grain_growth/isotropic/sparsePF/" \ +"coarsening/ostwald_ripening/isotropic/phase_field/" \ +"coarsening/zener_pinning/anisotropic/Monte_Carlo/" \ +"coarsening/zener_pinning/anisotropic/phase_field/" \ +"coarsening/zener_pinning/anisotropic/sparsePF/" \ +"coarsening/zener_pinning/isotropic/Monte_Carlo/" \ +"coarsening/zener_pinning/isotropic/phase_field/" \ +"coarsening/zener_pinning/isotropic/sparsePF/" \ +"phase_transitions/allen-cahn/" \ +"phase_transitions/cahn-hilliard/explicit/" \ +"phase_transitions/model_A/" \ +"phase_transitions/model_B/" \ +"phase_transitions/solidification/anisotropic/" \ +"phase_transitions/spinodal/" \ +"statistical_mechanics/Heisenberg/" \ +"statistical_mechanics/Ising/" \ +"statistical_mechanics/Potts/") # Skip phase_transitions/cahn-hilliard/convex_splitting -- too long # Skip differential_equations/elliptic/Poisson -- segmentation faults # Skip beginners_diffusion -- does not match execution pattern -for d in $exdirs +n=${#exdirs[@]} +for (( i=0; i<$n; i++ )) do - cd ${examples}/$d - printf "%-55s\t" $d exstart=$(date +%s) + j=$(($i+1)) + cd $examples/${exdirs[$i]} + printf "%2d/%2d %-50s\t" $j $n ${exdirs[$i]} (make -Bs || exit $?) && ((nSerBld++)) (make -Bs parallel || exit $?) && ((nParBld++)) - mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat $iters 500 >/dev/null && ((nParRun++)) - - for f in *.dat - do - mmsp2png --zoom $f >/dev/null - done - + if [[ $EXEC ]] + then + mpirun -np $CORES ./parallel --example 2 test.0000.dat && mpirun -np $CORES ./parallel test.0000.dat $ITERS $INTER >/dev/null && ((nParRun++)) + for f in *.dat + do + mmsp2png --zoom $f >/dev/null + done + fi if [[ $CLEAN ]] then - rm test.*.dat - rm test.*.png make -s clean + rm test.*.dat + if [[ $PURGE ]] + then + rm test.*.png + fi fi exfin=$(date +%s) From f6bd53db64fb6d37955f2bb17320f5b2b0a04191 Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Tue, 16 Feb 2016 01:27:27 -0500 Subject: [PATCH 33/83] add extra long option to build script. Monte Carlo examples may suffer deadlock, np-dependent --- test/build_examples.sh | 113 ++++++++++++++++++++++++----------------- 1 file changed, 67 insertions(+), 46 deletions(-) diff --git a/test/build_examples.sh b/test/build_examples.sh index 10c94a7..d17a1e0 100755 --- a/test/build_examples.sh +++ b/test/build_examples.sh @@ -4,21 +4,43 @@ tstart=$(date +%s) nSerBld=0 nParBld=0 nParRun=0 -iters=1000 +ITERS=1000 +INTER=500 +CORES=4 +EXEC=true cd ../examples +examples=$(pwd) + +echo -n "Building examples in serial and parallel" + while [[ $# > 0 ]] do key="$1" case $key in -c|--clean) + echo -n ", cleaning up after" + CLEAN=true + ;; + -p|--purge) + echo -n ", cleaning up after" CLEAN=true - shift # past argument + PURGE=true + ;; + -n|--noexec) + echo -n ", not executing" + EXEC=false ;; -l|--long) - iters=5000 - shift # past argument + echo -n ", taking 5,000 steps" + ITERS=5000 + INTER=1000 + ;; + -x|--extra) + echo -n ", taking 25,000 steps" + ITERS=25000 + INTER=5000 ;; *) # unknown option @@ -29,63 +51,62 @@ do shift # past argument or value done -if [[ $CLEAN ]] -then - echo "Building examples in serial and parallel, then deleting binaries." -else - echo "Building examples in serial and parallel, leaving binaries behind (specify --clean to clean up)." -fi echo -examples=$(pwd) - -exdirs="coarsening/grain_growth/anisotropic/Monte_Carlo/ -coarsening/grain_growth/anisotropic/phase_field/ -coarsening/grain_growth/anisotropic/sparsePF/ -coarsening/grain_growth/isotropic/Monte_Carlo/ -coarsening/grain_growth/isotropic/phase_field/ -coarsening/grain_growth/isotropic/sparsePF/ -coarsening/ostwald_ripening/isotropic/phase_field/ -coarsening/zener_pinning/anisotropic/Monte_Carlo/ -coarsening/zener_pinning/anisotropic/phase_field/ -coarsening/zener_pinning/anisotropic/sparsePF/ -coarsening/zener_pinning/isotropic/Monte_Carlo/ -coarsening/zener_pinning/isotropic/phase_field/ -coarsening/zener_pinning/isotropic/sparsePF/ -phase_transitions/allen-cahn/ -phase_transitions/cahn-hilliard/explicit/ -phase_transitions/model_A/ -phase_transitions/model_B/ -phase_transitions/solidification/anisotropic/ -phase_transitions/spinodal/ -statistical_mechanics/Heisenberg/ -statistical_mechanics/Ising/ -statistical_mechanics/Potts/" +exdirs=("coarsening/grain_growth/anisotropic/Monte_Carlo/" \ +"coarsening/grain_growth/anisotropic/phase_field/" \ +"coarsening/grain_growth/anisotropic/sparsePF/" \ +"coarsening/grain_growth/isotropic/Monte_Carlo/" \ +"coarsening/grain_growth/isotropic/phase_field/" \ +"coarsening/grain_growth/isotropic/sparsePF/" \ +"coarsening/ostwald_ripening/isotropic/phase_field/" \ +"coarsening/zener_pinning/anisotropic/Monte_Carlo/" \ +"coarsening/zener_pinning/anisotropic/phase_field/" \ +"coarsening/zener_pinning/anisotropic/sparsePF/" \ +"coarsening/zener_pinning/isotropic/Monte_Carlo/" \ +"coarsening/zener_pinning/isotropic/phase_field/" \ +"coarsening/zener_pinning/isotropic/sparsePF/" \ +"phase_transitions/allen-cahn/" \ +"phase_transitions/cahn-hilliard/explicit/" \ +"phase_transitions/model_A/" \ +"phase_transitions/model_B/" \ +"phase_transitions/solidification/anisotropic/" \ +"phase_transitions/spinodal/" \ +"statistical_mechanics/Heisenberg/" \ +"statistical_mechanics/Ising/" \ +"statistical_mechanics/Potts/") # Skip phase_transitions/cahn-hilliard/convex_splitting -- too long # Skip differential_equations/elliptic/Poisson -- segmentation faults # Skip beginners_diffusion -- does not match execution pattern -for d in $exdirs +n=${#exdirs[@]} +for (( i=0; i<$n; i++ )) do - cd ${examples}/$d - printf "%-55s\t" $d exstart=$(date +%s) + j=$(($i+1)) + cd $examples/${exdirs[$i]} + printf "%2d/%2d %-50s\t" $j $n ${exdirs[$i]} (make -Bs || exit $?) && ((nSerBld++)) (make -Bs parallel || exit $?) && ((nParBld++)) - mpirun -np 4 ./parallel --example 2 test.0000.dat && mpirun -np 4 ./parallel test.0000.dat $iters 500 >/dev/null && ((nParRun++)) - - for f in *.dat - do - mmsp2png --zoom $f >/dev/null - done - - if [[ $CLEAN ]] + if [[ $EXEC ]] then - rm test.*.dat + mpirun -np $CORES ./parallel --example 2 test.0000.dat && mpirun -np $CORES ./parallel test.0000.dat $ITERS $INTER >/dev/null && ((nParRun++)) rm test.*.png + for f in *.dat + do + mmsp2png --zoom $f >/dev/null + done + fi + if [[ $CLEAN ]] + then make -s clean + rm test.*.dat + if [[ $PURGE ]] + then + rm test.*.png + fi fi exfin=$(date +%s) From 56b24bbd95c9b2901b1241db7de2be2d595db2a9 Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Tue, 16 Feb 2016 01:36:09 -0500 Subject: [PATCH 34/83] remove old PNGs --- test/build_examples.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/build_examples.sh b/test/build_examples.sh index d17a1e0..7acbb4b 100755 --- a/test/build_examples.sh +++ b/test/build_examples.sh @@ -92,8 +92,8 @@ do (make -Bs parallel || exit $?) && ((nParBld++)) if [[ $EXEC ]] then - mpirun -np $CORES ./parallel --example 2 test.0000.dat && mpirun -np $CORES ./parallel test.0000.dat $ITERS $INTER >/dev/null && ((nParRun++)) rm test.*.png + mpirun -np $CORES ./parallel --example 2 test.0000.dat && mpirun -np $CORES ./parallel test.0000.dat $ITERS $INTER >/dev/null && ((nParRun++)) for f in *.dat do mmsp2png --zoom $f >/dev/null From e1d90c92d3cd9d53f21d902bb48ee1c993fd8bd7 Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Tue, 16 Feb 2016 12:29:32 -0500 Subject: [PATCH 35/83] constructive comments in test script --- test/build_examples.sh | 62 +++++++++++++++++++++++++++++++++--------- 1 file changed, 49 insertions(+), 13 deletions(-) diff --git a/test/build_examples.sh b/test/build_examples.sh index 7acbb4b..9fa93da 100755 --- a/test/build_examples.sh +++ b/test/build_examples.sh @@ -1,14 +1,46 @@ #!/bin/bash +# build_examples.sh +# This test script compiles each canonical* example in serial and parallel +# then executes the example program in 2D. The length of the test can be +# controlled by selecting default (1,000 steps), long (5,000 steps), or extra +# long (25,000 steps). The resulting checkpoints are then converted to PNG +# images to spot check problems in the code or environment, including execution +# errors and numerical instabilities. If you see something, say something. +# +# Questions/comments to trevor.keller@gmail.com (Trevor Keller) + +# * Canonical examples exclude the following directories: +# differential_equations/elliptic/Poisson -- segmentation faults +# beginners_diffusion -- does not match execution pattern + +# Valid flags are: +# --noexec: build in serial and parallel, but do not execute +# --clean: delete binary files after test completes +# --purge: delete binaries, data, and images after test completes +# --long: execute tests 5x longer than default +# --extra: execute tests 25x longer than default + +if [[ $(which mmsp2png) == "" ]] +then + # Necessary check for a valid installation. + # Consult doc/MMSP.manual.pdf if this fails. + echo "mmsp2png utility not found. Please check your installation." +fi + +# Initialize timer and completion counters tstart=$(date +%s) nSerBld=0 nParBld=0 nParRun=0 + +# Set execution parameters +EXEC=true ITERS=1000 INTER=500 CORES=4 -EXEC=true +# Get going cd ../examples examples=$(pwd) @@ -43,12 +75,11 @@ do INTER=5000 ;; *) - # unknown option echo "WARNING: Unknown option ${key}." echo ;; esac - shift # past argument or value + shift # pop first entry from command-line argument list, reduce $# by 1 done echo @@ -67,6 +98,7 @@ exdirs=("coarsening/grain_growth/anisotropic/Monte_Carlo/" \ "coarsening/zener_pinning/isotropic/phase_field/" \ "coarsening/zener_pinning/isotropic/sparsePF/" \ "phase_transitions/allen-cahn/" \ +"phase_transitions/cahn-hilliard/convex_splitting/" \ "phase_transitions/cahn-hilliard/explicit/" \ "phase_transitions/model_A/" \ "phase_transitions/model_B/" \ @@ -76,10 +108,6 @@ exdirs=("coarsening/grain_growth/anisotropic/Monte_Carlo/" \ "statistical_mechanics/Ising/" \ "statistical_mechanics/Potts/") -# Skip phase_transitions/cahn-hilliard/convex_splitting -- too long -# Skip differential_equations/elliptic/Poisson -- segmentation faults -# Skip beginners_diffusion -- does not match execution pattern - n=${#exdirs[@]} for (( i=0; i<$n; i++ )) do @@ -87,18 +115,25 @@ do j=$(($i+1)) cd $examples/${exdirs[$i]} printf "%2d/%2d %-50s\t" $j $n ${exdirs[$i]} - - (make -Bs || exit $?) && ((nSerBld++)) - (make -Bs parallel || exit $?) && ((nParBld++)) + echo $(date) >test.log + (make -B >>test.log || exit $?) && ((nSerBld++)) + (make -B parallel >>test.log || exit $?) && ((nParBld++)) if [[ $EXEC ]] then - rm test.*.png - mpirun -np $CORES ./parallel --example 2 test.0000.dat && mpirun -np $CORES ./parallel test.0000.dat $ITERS $INTER >/dev/null && ((nParRun++)) + # Remove existing images first. If the test fails, + # no images in the directory is an obvious red flag. + rm -f test.*.png + # Run the example in parallel, for speed. + mpirun -np $CORES ./parallel --example 2 test.0000.dat >>test.log \ + && mpirun -np $CORES ./parallel test.0000.dat $ITERS $INTER >>test.log \ + && ((nParRun++)) + # Show the result for f in *.dat do - mmsp2png --zoom $f >/dev/null + mmsp2png --zoom $f >>test.log done fi + # Clean up binaries and images if [[ $CLEAN ]] then make -s clean @@ -106,6 +141,7 @@ do if [[ $PURGE ]] then rm test.*.png + rm test.log fi fi From cd97d9e5650f81242956da19e710513aeccc884a Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Tue, 16 Feb 2016 01:36:09 -0500 Subject: [PATCH 36/83] remove old PNGs --- test/build_examples.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/build_examples.sh b/test/build_examples.sh index d17a1e0..9029f4d 100755 --- a/test/build_examples.sh +++ b/test/build_examples.sh @@ -92,8 +92,8 @@ do (make -Bs parallel || exit $?) && ((nParBld++)) if [[ $EXEC ]] then - mpirun -np $CORES ./parallel --example 2 test.0000.dat && mpirun -np $CORES ./parallel test.0000.dat $ITERS $INTER >/dev/null && ((nParRun++)) rm test.*.png + mpirun -np $CORES ./parallel --example 2 test.0000.dat && mpirun -np $CORES ./parallel test.0000.dat $ITERS $INTER >/dev/null && ((nParRun++)) for f in *.dat do mmsp2png --zoom $f >/dev/null @@ -105,7 +105,7 @@ do rm test.*.dat if [[ $PURGE ]] then - rm test.*.png + rm -f test.*.png fi fi From aafcf7d40531e67f5e198c90aa5e9a03cc019e70 Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Wed, 17 Feb 2016 16:53:33 -0500 Subject: [PATCH 37/83] aniso MC graingrowth working in serial --- .../anisotropic/Monte_Carlo/graingrowth.cpp | 53 +++++++++---------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp index 3e5a9aa..c504479 100644 --- a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp +++ b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp @@ -9,10 +9,10 @@ #ifndef GRAINGROWTH_UPDATE #define GRAINGROWTH_UPDATE -#include"MMSP.hpp" #include -#include"graingrowth.hpp" #include +#include"MMSP.hpp" +#include"graingrowth.hpp" namespace MMSP { @@ -33,7 +33,7 @@ void generate(int dim, const char* filename) } if (dim == 2) { - GRID2D initGrid(0, 0, 256, 0, 256); + GRID2D initGrid(0, 0, 128, 0, 128); for (int i = 0; i < nodes(initGrid); i++) initGrid(i) = rand() % 20; @@ -71,8 +71,10 @@ template void update(grid& mcGrid, int steps) MPI::COMM_WORLD.Barrier(); #endif + ghostswap(mcGrid); + /*---------------generate cells------------------*/ - int dimension_length = 0, number_of_lattice_cells = 1; + int dimension_length = 0, num_lattice_cells = 1; int lattice_cells_each_dimension[dim]; for (int i = 0; i < dim; i++) { dimension_length = x1(mcGrid, i) - x0(mcGrid, i); @@ -80,7 +82,7 @@ template void update(grid& mcGrid, int steps) lattice_cells_each_dimension[i] = dimension_length / 2 + 1; else lattice_cells_each_dimension[i] = 1 + (dimension_length % 2 == 0 ? dimension_length / 2 : dimension_length / 2 + 1); - number_of_lattice_cells *= lattice_cells_each_dimension[i]; + num_lattice_cells *= lattice_cells_each_dimension[i]; } vector x (dim, 0); @@ -96,7 +98,7 @@ template void update(grid& mcGrid, int steps) } - for (int j = 0; j < number_of_lattice_cells; j++) { + for (int j = 0; j < num_lattice_cells; j++) { int cell_coords_selected[dim]; if (dim == 2) { cell_coords_selected[dim - 1] = j % lattice_cells_each_dimension[dim - 1]; //1-indexed @@ -185,7 +187,7 @@ template void update(grid& mcGrid, int steps) // srand() is called exactly once in MMSP.main.hpp. Do not call it here. for (int hh = 0; hh < num_grids_to_flip[sublattice]; hh++) { - int cell_numbering = rand() % (number_of_lattice_cells); //choose a cell to flip, from 0 to num_cells_in_thread-1 + int cell_numbering = rand() % (num_lattice_cells); //choose a cell to flip, from 0 to num_cells_in_thread-1 int cell_coords_selected[dim]; if (dim == 2) { cell_coords_selected[dim - 1] = cell_numbering % lattice_cells_each_dimension[dim - 1]; //1-indexed @@ -243,13 +245,13 @@ template void update(grid& mcGrid, int steps) x[2]++; x[1]++; x[0]++; //1,1,1 + break; } } bool site_out_of_domain = false; for (int i = 0; i < dim; i++) { if (x[i] < x0(mcGrid, i) || x[i] >= x1(mcGrid, i)) { -// if (x[i]x1(mcGrid, i)-1){ site_out_of_domain = true; break;//break from the for int i loop } @@ -261,18 +263,18 @@ template void update(grid& mcGrid, int steps) double Q = 1.0e5; double R = 8.314; - double T = 673 + 50.0 / (g1(mcGrid, 0) - g0(mcGrid, 0)) * (x[0] - g0(mcGrid, 0)); // temperature is 673K at one half and 723K at the other half - double site_selection_probability = exp(-Q / R / T) / exp(-Q / R / 723); + double T = 673.0 + 50.0 / (g1(mcGrid, 0) - g0(mcGrid, 0)) * (x[0] - g0(mcGrid, 0)); // temperature is 673K at one half and 723K at the other half + double site_selection_probability = exp(-Q / R / T) / exp(-Q / R / 723.0); double rd = double(rand()) / double(RAND_MAX); if (rd > site_selection_probability) continue; //this site wont be selected - int spin1 = (mcGrid)(x); + int spin1 = mcGrid(x); // determine neighboring spinsss vector r(dim, 0); std::vector neighbors; neighbors.clear(); - unsigned int number_of_same_neighours = 0; + unsigned int num_same_neighours = 0; if (dim == 2) { for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) { @@ -281,10 +283,10 @@ template void update(grid& mcGrid, int steps) r[1] = x[1] + j; if (r[0] < g0(mcGrid, 0) || r[0] >= g1(mcGrid, 0) || r[1] < g0(mcGrid, 1) || r[1] >= g1(mcGrid, 1) ) // not periodic BC continue;// neighbour outside the global boundary, skip it. - int spin = (mcGrid)(r); + int spin = mcGrid(r); neighbors.push_back(spin); if (spin == spin1) - number_of_same_neighours++; + num_same_neighours++; } } } @@ -299,10 +301,10 @@ template void update(grid& mcGrid, int steps) if (r[0] < g0(mcGrid, 0) || r[0] >= g1(mcGrid, 0) || r[1] < g0(mcGrid, 1) || r[1] >= g1(mcGrid, 1) || r[2] < g0(mcGrid, 2) || r[2] >= g1(mcGrid, 2)) // not periodic BC continue;// neighbour outside the global boundary, skip it. - int spin = (mcGrid)(r); + int spin = mcGrid(r); neighbors.push_back(spin); if (spin == spin1) - number_of_same_neighours++; + num_same_neighours++; } } } @@ -310,7 +312,7 @@ template void update(grid& mcGrid, int steps) } //check if inside a grain - if (number_of_same_neighours == neighbors.size()) { //inside a grain + if (num_same_neighours == neighbors.size()) { //inside a grain continue;//continue for } //choose a random neighbor spin @@ -327,7 +329,7 @@ template void update(grid& mcGrid, int steps) r[1] = x[1] + j; if (r[0] < g0(mcGrid, 0) || r[0] >= g1(mcGrid, 0) || r[1] < g0(mcGrid, 1) || r[1] >= g1(mcGrid, 1) ) // not periodic BC continue;// neighbour outside the global boundary, skip it. - int spin = (mcGrid)(r); + int spin = mcGrid(r); dE += 1.0 / 2 * ((spin != spin2) - (spin != spin1)); }// if (!(i==0 && j==0)) } @@ -344,7 +346,7 @@ template void update(grid& mcGrid, int steps) if (r[0] < g0(mcGrid, 0) || r[0] >= g1(mcGrid, 0) || r[1] < g0(mcGrid, 1) || r[1] >= g1(mcGrid, 1) || r[2] < g0(mcGrid, 2) || r[2] >= g1(mcGrid, 2)) // not periodic BC continue;// neighbour outside the global boundary, skip it. - int spin = (mcGrid)(r); + int spin = mcGrid(r); dE += 1.0 / 2 * (spin != spin2) - (spin != spin1); } } @@ -353,19 +355,16 @@ template void update(grid& mcGrid, int steps) } // attempt a spin flip double r = double(rand()) / double(RAND_MAX); - double kT = 1.3803288e-23 * 273; - if (dE <= 0.0) (mcGrid)(x) = spin2; - else if (r < exp(-dE / kT)) (mcGrid)(x) = spin2; + double kT = 1.3803288e-23 * 273.0; + if (dE <= 0.0) mcGrid(x) = spin2; + else if (r < exp(-dE / kT)) mcGrid(x) = spin2; }//spin1!=spin2 } // hh -#ifdef MPI_VERSION + #ifdef MPI_VERSION MPI::COMM_WORLD.Barrier(); -#endif + #endif ghostswap(mcGrid, sublattice); // once looped over a "color", ghostswap. -#ifdef MPI_VERSION - MPI::COMM_WORLD.Barrier(); -#endif }//loop over sublattice }//loop over step }//update From 32b287a65949b472fe0fffc5e58ed8dfdad0f20d Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Wed, 17 Feb 2016 17:56:05 -0500 Subject: [PATCH 38/83] more verbose MPI_File error handling --- include/MMSP.grid.hpp | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/include/MMSP.grid.hpp b/include/MMSP.grid.hpp index 552fdcf..bce6389 100644 --- a/include/MMSP.grid.hpp +++ b/include/MMSP.grid.hpp @@ -1576,12 +1576,15 @@ class grid if (mpi_err != MPI_SUCCESS) { if (rank==0) MPI_File_delete(fname,MPI_INFO_NULL); + MPI::COMM_WORLD.Barrier(); mpi_err = MPI_File_open(MPI_COMM_WORLD, fname, MPI_MODE_WRONLY|MPI_MODE_EXCL|MPI_MODE_CREATE, MPI_INFO_NULL, &output); - assert(mpi_err==MPI_SUCCESS); - } - if (!output) { - std::cerr << "File output error: could not open " << fname << "." << std::endl; - exit(-1); + if (mpi_err != MPI_SUCCESS) { + char error_string[256]; + int length_of_error_string=256; + MPI_Error_string(mpi_err, error_string, &length_of_error_string); + fprintf(stderr, "%3d: %s\n", rank, error_string); + std::exit(-1); + } } // Generate MMSP header from rank 0 @@ -1697,7 +1700,6 @@ class grid unsigned int* writeranks=NULL; MPI_Request* recvrequests = NULL; MPI_Status* recvstatuses = NULL; - int mpi_err = 0; // get grid data to write const unsigned long size=write_buffer(databuffer); @@ -1892,24 +1894,19 @@ class grid if (rank==0) std::cout<<" Opening "< Date: Wed, 17 Feb 2016 18:55:32 -0500 Subject: [PATCH 39/83] spelling, naming, and ghostswapping in examples --- .../beginners_diffusion/MMSPDiffusion.cpp | 2 + .../beginners_diffusion/MMSPDiffusion2D.cpp | 1 + .../anisotropic/Monte_Carlo/graingrowth.cpp | 59 ++-- .../anisotropic/phase_field/graingrowth.cpp | 3 + .../anisotropic/sparsePF/graingrowth.cpp | 5 +- .../isotropic/Monte_Carlo/graingrowth.cpp | 107 ++++--- .../isotropic/phase_field/graingrowth.cpp | 7 +- .../isotropic/sparsePF/graingrowth.cpp | 6 +- .../isotropic/phase_field/ostwald.cpp | 2 + .../anisotropic/Monte_Carlo/zener.cpp | 11 +- .../anisotropic/phase_field/zener.cpp | 2 + .../anisotropic/sparsePF/zener.cpp | 8 +- .../isotropic/Monte_Carlo/zener.cpp | 11 +- .../isotropic/phase_field/zener.cpp | 2 + .../isotropic/sparsePF/zener.cpp | 7 +- .../allen-cahn/allen-cahn.cpp | 2 + .../cahn-hilliard/explicit/cahn-hilliard.cpp | 2 + .../phase_transitions/model_A/model_A.cpp | 2 + .../phase_transitions/model_B/model_B.cpp | 2 + .../anisotropic/solidification.cpp | 300 ++++++++---------- .../phase_transitions/spinodal/spinodal.cpp | 2 + .../Heisenberg/heisenberg.cpp | 11 +- .../statistical_mechanics/Ising/ising.cpp | 11 +- .../statistical_mechanics/Potts/potts.cpp | 11 +- 24 files changed, 303 insertions(+), 273 deletions(-) diff --git a/examples/beginners_diffusion/MMSPDiffusion.cpp b/examples/beginners_diffusion/MMSPDiffusion.cpp index 668e12d..421e7ba 100644 --- a/examples/beginners_diffusion/MMSPDiffusion.cpp +++ b/examples/beginners_diffusion/MMSPDiffusion.cpp @@ -39,6 +39,8 @@ int main(int argc, char* argv[]) b0(update,0) = Dirichlet; b1(update,0) = Dirichlet; + ghostswap(GRID); + for (int k=0; k bool OutsideDomainCheck(grid& mcGrid, vector* x) +template bool isOutsideDomain(const grid& mcGrid, const vector& x) { bool outside_domain = false; for (int i = 0; i < dim; i++) { - if ((*x)[i] < x0(mcGrid, i) || (*x)[i] > x1(mcGrid, i)) { + if (x[i] < x0(mcGrid, i) || x[i] > x1(mcGrid, i)) { outside_domain = true; break; } @@ -114,57 +114,57 @@ template void update(grid& mcGrid, int steps) if (dim == 2) { x_prim = x; - if (!OutsideDomainCheck(mcGrid, &x_prim)) num_grids_to_flip[0] += 1; + if (!isOutsideDomain(mcGrid, x_prim)) num_grids_to_flip[0] += 1; x_prim = x; x_prim[1] = x[1] + 1; //0,1 - if (!OutsideDomainCheck(mcGrid, &x_prim)) num_grids_to_flip[1] += 1; + if (!isOutsideDomain(mcGrid, x_prim)) num_grids_to_flip[1] += 1; x_prim = x; x_prim[0] = x[0] + 1; //1,0 - if (!OutsideDomainCheck(mcGrid, &x_prim)) num_grids_to_flip[2] += 1; + if (!isOutsideDomain(mcGrid, x_prim)) num_grids_to_flip[2] += 1; x_prim = x; x_prim[0] = x[0] + 1; x_prim[1] = x[1] + 1; //1,1 - if (!OutsideDomainCheck(mcGrid, &x_prim)) num_grids_to_flip[3] += 1; + if (!isOutsideDomain(mcGrid, x_prim)) num_grids_to_flip[3] += 1; } else if (dim == 3) { x_prim = x;//0,0,0 - if (!OutsideDomainCheck(mcGrid, &x_prim)) num_grids_to_flip[0] += 1; + if (!isOutsideDomain(mcGrid, x_prim)) num_grids_to_flip[0] += 1; x_prim = x; x_prim[2] = x[2] + 1; //0,0,1 - if (!OutsideDomainCheck(mcGrid, &x_prim)) num_grids_to_flip[1] += 1; + if (!isOutsideDomain(mcGrid, x_prim)) num_grids_to_flip[1] += 1; x_prim = x; x_prim[1] = x[1] + 1; //0,1,0 - if (!OutsideDomainCheck(mcGrid, &x_prim)) num_grids_to_flip[2] += 1; + if (!isOutsideDomain(mcGrid, x_prim)) num_grids_to_flip[2] += 1; x_prim = x; x_prim[2] = x[2] + 1; x_prim[1] = x[1] + 1; //0,1,1 - if (!OutsideDomainCheck(mcGrid, &x_prim)) num_grids_to_flip[3] += 1; + if (!isOutsideDomain(mcGrid, x_prim)) num_grids_to_flip[3] += 1; x_prim = x; x_prim[0] = x[0] + 1; //1,0,0 - if (!OutsideDomainCheck(mcGrid, &x_prim)) num_grids_to_flip[4] += 1; + if (!isOutsideDomain(mcGrid, x_prim)) num_grids_to_flip[4] += 1; x_prim = x; x_prim[2] = x[2] + 1; x_prim[0] = x[0] + 1; //1,0,1 - if (!OutsideDomainCheck(mcGrid, &x_prim)) num_grids_to_flip[5] += 1; + if (!isOutsideDomain(mcGrid, x_prim)) num_grids_to_flip[5] += 1; x_prim = x; x_prim[1] = x[1] + 1; x_prim[0] = x[0] + 1; //1,1,0 - if (!OutsideDomainCheck(mcGrid, &x_prim)) num_grids_to_flip[6] += 1; + if (!isOutsideDomain(mcGrid, x_prim)) num_grids_to_flip[6] += 1; x_prim = x; x_prim[2] = x[2] + 1; x_prim[1] = x[1] + 1; x_prim[0] = x[0] + 1; //1,1,1 - if (!OutsideDomainCheck(mcGrid, &x_prim)) num_grids_to_flip[7] += 1; + if (!isOutsideDomain(mcGrid, x_prim)) num_grids_to_flip[7] += 1; } }// for int j @@ -270,11 +270,11 @@ template void update(grid& mcGrid, int steps) if (rd > site_selection_probability) continue; //this site wont be selected int spin1 = mcGrid(x); - // determine neighboring spinsss + // determine neighboring spins vector r(dim, 0); std::vector neighbors; neighbors.clear(); - unsigned int num_same_neighours = 0; + unsigned int num_same_neighbors = 0; if (dim == 2) { for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) { @@ -282,11 +282,11 @@ template void update(grid& mcGrid, int steps) r[0] = x[0] + i; r[1] = x[1] + j; if (r[0] < g0(mcGrid, 0) || r[0] >= g1(mcGrid, 0) || r[1] < g0(mcGrid, 1) || r[1] >= g1(mcGrid, 1) ) // not periodic BC - continue;// neighbour outside the global boundary, skip it. + continue;// neighbor outside the global boundary, skip it. int spin = mcGrid(r); neighbors.push_back(spin); if (spin == spin1) - num_same_neighours++; + num_same_neighbors++; } } } @@ -300,11 +300,11 @@ template void update(grid& mcGrid, int steps) r[2] = x[2] + k; if (r[0] < g0(mcGrid, 0) || r[0] >= g1(mcGrid, 0) || r[1] < g0(mcGrid, 1) || r[1] >= g1(mcGrid, 1) || r[2] < g0(mcGrid, 2) || r[2] >= g1(mcGrid, 2)) // not periodic BC - continue;// neighbour outside the global boundary, skip it. + continue;// neighbor outside the global boundary, skip it. int spin = mcGrid(r); neighbors.push_back(spin); if (spin == spin1) - num_same_neighours++; + num_same_neighbors++; } } } @@ -312,7 +312,7 @@ template void update(grid& mcGrid, int steps) } //check if inside a grain - if (num_same_neighours == neighbors.size()) { //inside a grain + if (num_same_neighbors == neighbors.size()) { //inside a grain continue;//continue for } //choose a random neighbor spin @@ -328,9 +328,9 @@ template void update(grid& mcGrid, int steps) r[0] = x[0] + i; r[1] = x[1] + j; if (r[0] < g0(mcGrid, 0) || r[0] >= g1(mcGrid, 0) || r[1] < g0(mcGrid, 1) || r[1] >= g1(mcGrid, 1) ) // not periodic BC - continue;// neighbour outside the global boundary, skip it. + continue;// neighbor outside the global boundary, skip it. int spin = mcGrid(r); - dE += 1.0 / 2 * ((spin != spin2) - (spin != spin1)); + dE += 0.5 * ((spin != spin2) - (spin != spin1)); }// if (!(i==0 && j==0)) } } @@ -345,9 +345,9 @@ template void update(grid& mcGrid, int steps) r[2] = x[2] + k; if (r[0] < g0(mcGrid, 0) || r[0] >= g1(mcGrid, 0) || r[1] < g0(mcGrid, 1) || r[1] >= g1(mcGrid, 1) || r[2] < g0(mcGrid, 2) || r[2] >= g1(mcGrid, 2)) // not periodic BC - continue;// neighbour outside the global boundary, skip it. + continue;// neighbor outside the global boundary, skip it. int spin = mcGrid(r); - dE += 1.0 / 2 * (spin != spin2) - (spin != spin1); + dE += 0.5 * (spin != spin2) - (spin != spin1); } } } @@ -356,8 +356,11 @@ template void update(grid& mcGrid, int steps) // attempt a spin flip double r = double(rand()) / double(RAND_MAX); double kT = 1.3803288e-23 * 273.0; - if (dE <= 0.0) mcGrid(x) = spin2; - else if (r < exp(-dE / kT)) mcGrid(x) = spin2; + + if (dE <= 0.0) + mcGrid(x) = spin2; + else if (r < exp(-dE / kT)) + mcGrid(x) = spin2; }//spin1!=spin2 } // hh diff --git a/examples/coarsening/grain_growth/anisotropic/phase_field/graingrowth.cpp b/examples/coarsening/grain_growth/anisotropic/phase_field/graingrowth.cpp index 125e96b..392d43c 100644 --- a/examples/coarsening/grain_growth/anisotropic/phase_field/graingrowth.cpp +++ b/examples/coarsening/grain_growth/anisotropic/phase_field/graingrowth.cpp @@ -90,6 +90,9 @@ template void update(grid >& oldGrid, int st #ifdef MPI_VERSION rank = MPI::COMM_WORLD.Get_rank(); #endif + + ghostswap(oldGrid); + grid > newGrid(oldGrid); double dt = 0.01; diff --git a/examples/coarsening/grain_growth/anisotropic/sparsePF/graingrowth.cpp b/examples/coarsening/grain_growth/anisotropic/sparsePF/graingrowth.cpp index 7021915..ecf4f73 100644 --- a/examples/coarsening/grain_growth/anisotropic/sparsePF/graingrowth.cpp +++ b/examples/coarsening/grain_growth/anisotropic/sparsePF/graingrowth.cpp @@ -85,11 +85,14 @@ template void update(grid >& oldGrid, int st double width = 8.0; double epsilon = 1.0e-8; + ghostswap(oldGrid); + + grid > newGrid(oldGrid); + for (int step=0; step > newGrid(oldGrid); for (int n=0; n x = position(oldGrid,n); diff --git a/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp b/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp index a2f7454..78fa31d 100644 --- a/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp +++ b/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp @@ -33,21 +33,25 @@ void generate(int dim, const char* filename) for (int i = 0; i < nodes(initGrid); i++) initGrid(i) = rand() % 20; + output(initGrid, filename); + } else if (dim == 3) { GRID3D initGrid(0, 0, 32, 0, 32, 0, 32); for (int i = 0; i < nodes(initGrid); i++) initGrid(i) = rand() % 20; + output(initGrid, filename); + } } -template bool OutsideDomainCheck(grid& mcGrid, vector* x) +template bool isOutsideDomain(const grid& mcGrid, const vector& x) { bool outside_domain = false; for (int i = 0; i < dim; i++) { - if ((*x)[i] < x0(mcGrid, i) || (*x)[i] > x1(mcGrid, i)) { + if (x[i] < x0(mcGrid, i) || x[i] > x1(mcGrid, i)) { outside_domain = true; break; } @@ -65,8 +69,10 @@ template void update(grid& mcGrid, int steps) MPI::COMM_WORLD.Barrier(); #endif + ghostswap(mcGrid); + /*---------------generate cells------------------*/ - int dimension_length = 0, number_of_lattice_cells = 1; + int dimension_length = 0, num_lattice_cells = 1; int lattice_cells_each_dimension[dim]; for (int i = 0; i < dim; i++) { dimension_length = x1(mcGrid, i) - x0(mcGrid, i); @@ -74,7 +80,7 @@ template void update(grid& mcGrid, int steps) lattice_cells_each_dimension[i] = dimension_length / 2 + 1; else lattice_cells_each_dimension[i] = 1 + (dimension_length % 2 == 0 ? dimension_length / 2 : dimension_length / 2 + 1); - number_of_lattice_cells *= lattice_cells_each_dimension[i]; + num_lattice_cells *= lattice_cells_each_dimension[i]; } vector x (dim, 0); @@ -90,7 +96,7 @@ template void update(grid& mcGrid, int steps) } - for (int j = 0; j < number_of_lattice_cells; j++) { + for (int j = 0; j < num_lattice_cells; j++) { int cell_coords_selected[dim]; if (dim == 2) { cell_coords_selected[dim - 1] = j % lattice_cells_each_dimension[dim - 1]; //1-indexed @@ -106,57 +112,57 @@ template void update(grid& mcGrid, int steps) if (dim == 2) { x_prim = x; - if (!OutsideDomainCheck(mcGrid, &x_prim)) num_of_grids_to_flip[0] += 1; + if (!isOutsideDomain(mcGrid, x_prim)) num_of_grids_to_flip[0] += 1; x_prim = x; x_prim[1] = x[1] + 1; //0,1 - if (!OutsideDomainCheck(mcGrid, &x_prim)) num_of_grids_to_flip[1] += 1; + if (!isOutsideDomain(mcGrid, x_prim)) num_of_grids_to_flip[1] += 1; x_prim = x; x_prim[0] = x[0] + 1; //1,0 - if (!OutsideDomainCheck(mcGrid, &x_prim)) num_of_grids_to_flip[2] += 1; + if (!isOutsideDomain(mcGrid, x_prim)) num_of_grids_to_flip[2] += 1; x_prim = x; x_prim[0] = x[0] + 1; x_prim[1] = x[1] + 1; //1,1 - if (!OutsideDomainCheck(mcGrid, &x_prim)) num_of_grids_to_flip[3] += 1; + if (!isOutsideDomain(mcGrid, x_prim)) num_of_grids_to_flip[3] += 1; } else if (dim == 3) { x_prim = x;//0,0,0 - if (!OutsideDomainCheck(mcGrid, &x_prim)) num_of_grids_to_flip[0] += 1; + if (!isOutsideDomain(mcGrid, x_prim)) num_of_grids_to_flip[0] += 1; x_prim = x; x_prim[2] = x[2] + 1; //0,0,1 - if (!OutsideDomainCheck(mcGrid, &x_prim)) num_of_grids_to_flip[1] += 1; + if (!isOutsideDomain(mcGrid, x_prim)) num_of_grids_to_flip[1] += 1; x_prim = x; x_prim[1] = x[1] + 1; //0,1,0 - if (!OutsideDomainCheck(mcGrid, &x_prim)) num_of_grids_to_flip[2] += 1; + if (!isOutsideDomain(mcGrid, x_prim)) num_of_grids_to_flip[2] += 1; x_prim = x; x_prim[2] = x[2] + 1; x_prim[1] = x[1] + 1; //0,1,1 - if (!OutsideDomainCheck(mcGrid, &x_prim)) num_of_grids_to_flip[3] += 1; + if (!isOutsideDomain(mcGrid, x_prim)) num_of_grids_to_flip[3] += 1; x_prim = x; x_prim[0] = x[0] + 1; //1,0,0 - if (!OutsideDomainCheck(mcGrid, &x_prim)) num_of_grids_to_flip[4] += 1; + if (!isOutsideDomain(mcGrid, x_prim)) num_of_grids_to_flip[4] += 1; x_prim = x; x_prim[2] = x[2] + 1; x_prim[0] = x[0] + 1; //1,0,1 - if (!OutsideDomainCheck(mcGrid, &x_prim)) num_of_grids_to_flip[5] += 1; + if (!isOutsideDomain(mcGrid, x_prim)) num_of_grids_to_flip[5] += 1; x_prim = x; x_prim[1] = x[1] + 1; x_prim[0] = x[0] + 1; //1,1,0 - if (!OutsideDomainCheck(mcGrid, &x_prim)) num_of_grids_to_flip[6] += 1; + if (!isOutsideDomain(mcGrid, x_prim)) num_of_grids_to_flip[6] += 1; x_prim = x; x_prim[2] = x[2] + 1; x_prim[1] = x[1] + 1; x_prim[0] = x[0] + 1; //1,1,1 - if (!OutsideDomainCheck(mcGrid, &x_prim)) num_of_grids_to_flip[7] += 1; + if (!isOutsideDomain(mcGrid, x_prim)) num_of_grids_to_flip[7] += 1; } }// for int j @@ -180,7 +186,7 @@ template void update(grid& mcGrid, int steps) // srand() is called exactly once in MMSP.main.hpp. Do not call it here. for (int hh = 0; hh < num_of_grids_to_flip[sublattice]; hh++) { - int cell_numbering = rand() % (number_of_lattice_cells); //choose a cell to flip, from 0 to num_of_cells_in_thread-1 + int cell_numbering = rand() % (num_lattice_cells); //choose a cell to flip, from 0 to num_of_cells_in_thread-1 int cell_coords_selected[dim]; if (dim == 2) { cell_coords_selected[dim - 1] = cell_numbering % lattice_cells_each_dimension[dim - 1]; //1-indexed @@ -244,7 +250,6 @@ template void update(grid& mcGrid, int steps) bool site_out_of_domain = false; for (int i = 0; i < dim; i++) { if (x[i] < x0(mcGrid, i) || x[i] >= x1(mcGrid, i)) { -// if (x[i]x1(mcGrid, i)-1){ site_out_of_domain = true; break;//break from the for int i loop } @@ -259,24 +264,25 @@ template void update(grid& mcGrid, int steps) double rd = double(rand()) / double(RAND_MAX); if (rd > site_selection_probability) continue; //this site wont be selected - int spin1 = (mcGrid)(x); - // determine neighboring spinsss + int spin1 = mcGrid(x); + // determine neighboring spins vector r(dim, 0); std::vector neighbors; neighbors.clear(); - unsigned int number_of_same_neighours = 0; + unsigned int num_same_neighbors = 0; if (dim == 2) { for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) { if (!(i == 0 && j == 0)) { r[0] = x[0] + i; r[1] = x[1] + j; - if (r[0] < g0(mcGrid, 0) || r[0] >= g1(mcGrid, 0) || r[1] < g0(mcGrid, 1) || r[1] >= g1(mcGrid, 1) ) // not periodic BC - continue;// neighbour outside the global boundary, skip it. - int spin = (mcGrid)(r); + if (r[0] < g0(mcGrid, 0) || r[0] >= g1(mcGrid, 0) || + r[1] < g0(mcGrid, 1) || r[1] >= g1(mcGrid, 1) ) // not periodic BC + continue;// neighbor outside the global boundary, skip it. + int spin = mcGrid(r); neighbors.push_back(spin); if (spin == spin1) - number_of_same_neighours++; + num_same_neighbors++; } } } @@ -288,13 +294,14 @@ template void update(grid& mcGrid, int steps) r[0] = x[0] + i; r[1] = x[1] + j; r[2] = x[2] + k; - if (r[0] < g0(mcGrid, 0) || r[0] >= g1(mcGrid, 0) || r[1] < g0(mcGrid, 1) || r[1] >= g1(mcGrid, 1) || - r[2] < g0(mcGrid, 2) || r[2] >= g1(mcGrid, 2)) // not periodic BC - continue;// neighbour outside the global boundary, skip it. - int spin = (mcGrid)(r); + if (r[0] < g0(mcGrid, 0) || r[0] >= g1(mcGrid, 0) || + r[1] < g0(mcGrid, 1) || r[1] >= g1(mcGrid, 1) || + r[2] < g0(mcGrid, 2) || r[2] >= g1(mcGrid, 2)) // not periodic BC + continue;// neighbor outside the global boundary, skip it. + int spin = mcGrid(r); neighbors.push_back(spin); if (spin == spin1) - number_of_same_neighours++; + num_same_neighbors++; } } } @@ -302,7 +309,7 @@ template void update(grid& mcGrid, int steps) } //check if inside a grain - if (number_of_same_neighours == neighbors.size()) { //inside a grain + if (num_same_neighbors == neighbors.size()) { //inside a grain continue;//continue for } //choose a random neighbor spin @@ -317,10 +324,11 @@ template void update(grid& mcGrid, int steps) if (!(i == 0 && j == 0)) { r[0] = x[0] + i; r[1] = x[1] + j; - if (r[0] < g0(mcGrid, 0) || r[0] >= g1(mcGrid, 0) || r[1] < g0(mcGrid, 1) || r[1] >= g1(mcGrid, 1) ) // not periodic BC - continue;// neighbour outside the global boundary, skip it. - int spin = (mcGrid)(r); - dE += 1.0 / 2 * ((spin != spin2) - (spin != spin1)); + if (r[0] < g0(mcGrid, 0) || r[0] >= g1(mcGrid, 0) || + r[1] < g0(mcGrid, 1) || r[1] >= g1(mcGrid, 1) ) // not periodic BC + continue;// neighbor outside the global boundary, skip it. + int spin = mcGrid(r); + dE += 0.5 * ((spin != spin2) - (spin != spin1)); }// if (!(i==0 && j==0)) } } @@ -333,11 +341,12 @@ template void update(grid& mcGrid, int steps) r[0] = x[0] + i; r[1] = x[1] + j; r[2] = x[2] + k; - if (r[0] < g0(mcGrid, 0) || r[0] >= g1(mcGrid, 0) || r[1] < g0(mcGrid, 1) || r[1] >= g1(mcGrid, 1) || - r[2] < g0(mcGrid, 2) || r[2] >= g1(mcGrid, 2)) // not periodic BC - continue;// neighbour outside the global boundary, skip it. - int spin = (mcGrid)(r); - dE += 1.0 / 2 * (spin != spin2) - (spin != spin1); + if (r[0] < g0(mcGrid, 0) || r[0] >= g1(mcGrid, 0) || + r[1] < g0(mcGrid, 1) || r[1] >= g1(mcGrid, 1) || + r[2] < g0(mcGrid, 2) || r[2] >= g1(mcGrid, 2)) // not periodic BC + continue;// neighbor outside the global boundary, skip it. + int spin = mcGrid(r); + dE += 0.5 * ((spin != spin2) - (spin != spin1)); } } } @@ -345,19 +354,19 @@ template void update(grid& mcGrid, int steps) } // attempt a spin flip double r = double(rand()) / double(RAND_MAX); - double kT = 1.3803288e-23 * 273; - if (dE <= 0.0) (mcGrid)(x) = spin2; - else if (r < exp(-dE / kT)) (mcGrid)(x) = spin2; + double kT = 1.3803288e-23 * 273.0; + + if (dE <= 0.0) + mcGrid(x) = spin2; + else if (r < exp(-dE / kT)) + mcGrid(x) = spin2; }//spin1!=spin2 } // hh -#ifdef MPI_VERSION + #ifdef MPI_VERSION MPI::COMM_WORLD.Barrier(); -#endif + #endif ghostswap(mcGrid, sublattice); // once looped over a "color", ghostswap. -#ifdef MPI_VERSION - MPI::COMM_WORLD.Barrier(); -#endif }//loop over sublattice }//loop over step }//update diff --git a/examples/coarsening/grain_growth/isotropic/phase_field/graingrowth.cpp b/examples/coarsening/grain_growth/isotropic/phase_field/graingrowth.cpp index baf831d..1a66ac1 100644 --- a/examples/coarsening/grain_growth/isotropic/phase_field/graingrowth.cpp +++ b/examples/coarsening/grain_growth/isotropic/phase_field/graingrowth.cpp @@ -74,7 +74,10 @@ template void update(grid >& oldGrid, int st #ifdef MPI_VERSION rank = MPI::COMM_WORLD.Get_rank(); #endif - grid > newGrid(oldGrid); + + ghostswap(oldGrid); + + grid > newGrid(oldGrid); double dt = 0.01; @@ -100,8 +103,8 @@ template void update(grid >& oldGrid, int st newGrid(i)[j] = phi-dt*(-phi-pow(phi,3)+2.0*(phi*sum-lap[j])); } } - swap(oldGrid,newGrid); ghostswap(oldGrid); + swap(oldGrid,newGrid); } } diff --git a/examples/coarsening/grain_growth/isotropic/sparsePF/graingrowth.cpp b/examples/coarsening/grain_growth/isotropic/sparsePF/graingrowth.cpp index e1fd8d8..72e7853 100644 --- a/examples/coarsening/grain_growth/isotropic/sparsePF/graingrowth.cpp +++ b/examples/coarsening/grain_growth/isotropic/sparsePF/graingrowth.cpp @@ -60,14 +60,16 @@ template void update(grid >& oldGrid, int st rank = MPI::COMM_WORLD.Get_rank(); #endif + ghostswap(oldGrid); + + grid > newGrid(oldGrid); + double dt = 0.01; double epsilon = 1.0e-8; for (int step=0; step > newGrid(oldGrid); for (int n=0; n x = position(oldGrid,n); diff --git a/examples/coarsening/ostwald_ripening/isotropic/phase_field/ostwald.cpp b/examples/coarsening/ostwald_ripening/isotropic/phase_field/ostwald.cpp index 693ed64..dd9b613 100644 --- a/examples/coarsening/ostwald_ripening/isotropic/phase_field/ostwald.cpp +++ b/examples/coarsening/ostwald_ripening/isotropic/phase_field/ostwald.cpp @@ -57,6 +57,8 @@ template void update(grid >& oldGrid, int st rank = MPI::COMM_WORLD.Get_rank(); #endif + ghostswap(oldGrid); + grid > newGrid(oldGrid); grid wspace(oldGrid,1); diff --git a/examples/coarsening/zener_pinning/anisotropic/Monte_Carlo/zener.cpp b/examples/coarsening/zener_pinning/anisotropic/Monte_Carlo/zener.cpp index 73a94d2..d7e7c9a 100644 --- a/examples/coarsening/zener_pinning/anisotropic/Monte_Carlo/zener.cpp +++ b/examples/coarsening/zener_pinning/anisotropic/Monte_Carlo/zener.cpp @@ -89,6 +89,8 @@ template void update(grid& mcGrid, int steps) rank = MPI::COMM_WORLD.Get_rank(); #endif + ghostswap(mcGrid); + const double kT = (dim==3)?0.75:0.50; int gss = int(nodes(mcGrid)); @@ -188,11 +190,14 @@ template void update(grid& mcGrid, int steps) // attempt a spin flip double r = double(rand())/double(RAND_MAX); - if (dE<=0.0 and r0.0 and r0.0 and r void update(grid >& oldGrid, int st rank = MPI::COMM_WORLD.Get_rank(); #endif + ghostswap(oldGrid); + grid > newGrid(oldGrid); double dt = 0.01; diff --git a/examples/coarsening/zener_pinning/anisotropic/sparsePF/zener.cpp b/examples/coarsening/zener_pinning/anisotropic/sparsePF/zener.cpp index 67e08da..52dc175 100644 --- a/examples/coarsening/zener_pinning/anisotropic/sparsePF/zener.cpp +++ b/examples/coarsening/zener_pinning/anisotropic/sparsePF/zener.cpp @@ -98,6 +98,10 @@ template void update(grid >& oldGrid, int st rank = MPI::COMM_WORLD.Get_rank(); #endif + ghostswap(oldGrid); + + grid > newGrid(oldGrid); + double dt = 0.01; double width = 8.0; double epsilon = 1.0e-8; @@ -106,10 +110,8 @@ template void update(grid >& oldGrid, int st if (rank==0) print_progress(step, steps); - grid > newGrid(oldGrid); - for (int n=0; n s; vector x = position(oldGrid,n); diff --git a/examples/coarsening/zener_pinning/isotropic/Monte_Carlo/zener.cpp b/examples/coarsening/zener_pinning/isotropic/Monte_Carlo/zener.cpp index 53da390..5c180a8 100644 --- a/examples/coarsening/zener_pinning/isotropic/Monte_Carlo/zener.cpp +++ b/examples/coarsening/zener_pinning/isotropic/Monte_Carlo/zener.cpp @@ -100,6 +100,8 @@ template void update(grid& spinGrid, int steps) rank = MPI::COMM_WORLD.Get_rank(); #endif + ghostswap(spinGrid); + const double kT = (dim==3)?0.75:0.50; int gss = int(sqrt(nodes(spinGrid))); // srand() is called exactly once in MMSP.main.hpp. Do not call it here. @@ -169,11 +171,14 @@ template void update(grid& spinGrid, int steps) // attempt a spin flip double r = double(rand())/double(RAND_MAX); - if (dE<=0.0) spinGrid(p) = spin2; - else if (r void update(grid >& oldGrid, int st rank = MPI::COMM_WORLD.Get_rank(); #endif + ghostswap(oldGrid); + grid > newGrid(oldGrid); double dt = 0.01; diff --git a/examples/coarsening/zener_pinning/isotropic/sparsePF/zener.cpp b/examples/coarsening/zener_pinning/isotropic/sparsePF/zener.cpp index b52ff69..db3f3f0 100644 --- a/examples/coarsening/zener_pinning/isotropic/sparsePF/zener.cpp +++ b/examples/coarsening/zener_pinning/isotropic/sparsePF/zener.cpp @@ -97,6 +97,10 @@ template void update(grid >& oldGrid, int st rank = MPI::COMM_WORLD.Get_rank(); #endif + ghostswap(oldGrid); + + grid > newGrid(oldGrid); + double dt = 0.01; double epsilon = 1.0e-8; @@ -104,9 +108,6 @@ template void update(grid >& oldGrid, int st if (rank==0) print_progress(step, steps); - // update grid must be overwritten each time - grid > newGrid(oldGrid); - for (int n=0; n x = position(oldGrid,n); diff --git a/examples/phase_transitions/allen-cahn/allen-cahn.cpp b/examples/phase_transitions/allen-cahn/allen-cahn.cpp index 1ae0a99..4588e60 100644 --- a/examples/phase_transitions/allen-cahn/allen-cahn.cpp +++ b/examples/phase_transitions/allen-cahn/allen-cahn.cpp @@ -48,6 +48,8 @@ template void update(grid& oldGrid, int steps) rank = MPI::COMM_WORLD.Get_rank(); #endif + ghostswap(oldGrid); + grid newGrid(oldGrid); double r = 1.0; diff --git a/examples/phase_transitions/cahn-hilliard/explicit/cahn-hilliard.cpp b/examples/phase_transitions/cahn-hilliard/explicit/cahn-hilliard.cpp index 6fe2205..139ed26 100644 --- a/examples/phase_transitions/cahn-hilliard/explicit/cahn-hilliard.cpp +++ b/examples/phase_transitions/cahn-hilliard/explicit/cahn-hilliard.cpp @@ -48,6 +48,8 @@ template void update(grid& oldGrid, int steps) rank = MPI::COMM_WORLD.Get_rank(); #endif + ghostswap(oldGrid); + grid newGrid(oldGrid); grid temp(oldGrid); diff --git a/examples/phase_transitions/model_A/model_A.cpp b/examples/phase_transitions/model_A/model_A.cpp index 88dd775..6c80120 100644 --- a/examples/phase_transitions/model_A/model_A.cpp +++ b/examples/phase_transitions/model_A/model_A.cpp @@ -75,6 +75,8 @@ template void update(grid& oldGrid, int steps) rank = MPI::COMM_WORLD.Get_rank(); #endif + ghostswap(oldGrid); + grid newGrid(oldGrid); T r = 1.0; diff --git a/examples/phase_transitions/model_B/model_B.cpp b/examples/phase_transitions/model_B/model_B.cpp index 8b7889a..1e4a2b6 100644 --- a/examples/phase_transitions/model_B/model_B.cpp +++ b/examples/phase_transitions/model_B/model_B.cpp @@ -75,6 +75,8 @@ template void update(grid& oldGrid, int steps) rank = MPI::COMM_WORLD.Get_rank(); #endif + ghostswap(oldGrid); + grid newGrid(oldGrid); grid temp(oldGrid); diff --git a/examples/phase_transitions/solidification/anisotropic/solidification.cpp b/examples/phase_transitions/solidification/anisotropic/solidification.cpp index 8c0374a..0c8ac6d 100644 --- a/examples/phase_transitions/solidification/anisotropic/solidification.cpp +++ b/examples/phase_transitions/solidification/anisotropic/solidification.cpp @@ -10,186 +10,146 @@ #include"MMSP.hpp" #include"solidification.hpp" -namespace MMSP{ +namespace MMSP +{ void generate(int dim, const char* filename) { - const int L=128; - const double deltaX=0.025; - const double undercooling=-0.5; - if (dim==2) - { - GRID2D initGrid(2,0,L,0,L); - for (int d=0; d x = position(initGrid,i); - int r=sqrt(pow(x[0]-L/2,2)+pow(x[1]-L/2,2)); - if (r<=R) initGrid(i)[0]=1.; - else initGrid(i)[0]=0.; - } - output(initGrid,filename); - } - else - { - std::cerr<<"Anisotropic solidification code is only implemented for 2D."< x = position(initGrid,i); + int r=sqrt(pow(x[0]-L/2,2)+pow(x[1]-L/2,2)); + if (r<=R) initGrid(i)[0]=1.; + else initGrid(i)[0]=0.; + } + output(initGrid,filename); + } else { + std::cerr<<"Anisotropic solidification code is only implemented for 2D."< void update(grid >& refGrid, int steps) +template void update(grid >& oldGrid, int steps) { - int id=0; - int np=1; - #ifdef MPI_VERSION - id=MPI::COMM_WORLD.Get_rank(); - np=MPI::COMM_WORLD.Get_size(); - #endif - - static int iterations=1; - static grid<2,T> oldGrid(1,g0(refGrid,0),g1(refGrid,0),g0(refGrid,1),g1(refGrid,1)); - if (iterations==1) - for (int i=0; i > newGrid(refGrid); - double dt=5e-5; // time-step - double theta=0.; // angle relative to lab frame - double c=0.02; // degree of anisotropy - double N=6.; // symmetry - double alpha=0.015; // gradient-energy coefficient - double tau=3e-4; // time normalization constant - double k1=0.9; - double k2=20.; - double DiffT=2.25; // thermal diffusivity - double CFL=tau/(2*alpha*alpha*(1./pow(dx(refGrid,0),2)+1./pow(dx(refGrid,1),2))); // Courant-Friedrich-Lewy condition on dt - - if (dt>0.5*CFL) - { - if (id==0) std::cout<<"dt="<0.5*CFL) dt*=3./4; - if (id==0) std::cout< > Dgradphi(refGrid); - - for (int i=0; i x=position(refGrid,i); - - // calculate grad(phi) - vector gradphi(dim,0.); // (0,0) - for (int d=0; d x = position(refGrid,i); - - // Update phase field - T divDgradphi = 0.; - for (int d=0; d old=refGrid(i); - T m_phi=old[0]-0.5-(k1/M_PI)*atan(k2*old[1]); - // Semi-implicit scheme per Warren 2003 - if (m_phi>0) - { - newGrid(x)[0] = ((m_phi+tau/dt)*old[0]+divDgradphi)/(tau/dt+old[0]*m_phi); - } - else - { - newGrid(x)[0] = (old[0]*tau/dt+divDgradphi)/(tau/dt-(1.-old[0])*m_phi); - } - // Fully explicit forward-Euler discretization - //newGrid(x)[0] = refGrid(i)[0] + dt*dphidt/tau; - - // Update undercooling field - T lapT=0; - for (int d=0; d refGrid(oldGrid,0); // constructor copies only field 0 + grid > newGrid(oldGrid); + + double dt=5e-5; // time-step + double theta=0.; // angle relative to lab frame + double c=0.02; // degree of anisotropy + double N=6.; // symmetry + double alpha=0.015; // gradient-energy coefficient + double tau=3e-4; // time normalization constant + double k1=0.9; + double k2=20.; + double DiffT=2.25; // thermal diffusivity + double CFL=tau/(2*alpha*alpha*(1./pow(dx(oldGrid,0),2)+1./pow(dx(oldGrid,1),2))); // Courant-Friedrich-Lewy condition on dt + + if (dt>0.5*CFL) { + if (id==0) std::cout<<"dt="<0.5*CFL) dt*=3./4; + if (id==0) std::cout< > Dgradphi(oldGrid); + + for (int i=0; i x=position(oldGrid,i); + + // calculate grad(phi) + vector gradphi(dim,0.); // (0,0) + for (int d=0; d x = position(oldGrid,i); + + // Update phase field + T divDgradphi = 0.; + for (int d=0; d old=oldGrid(i); + T m_phi=old[0]-0.5-(k1/M_PI)*atan(k2*old[1]); + // Semi-implicit scheme per Warren 2003 + if (m_phi>0) { + newGrid(x)[0] = ((m_phi+tau/dt)*old[0]+divDgradphi)/(tau/dt+old[0]*m_phi); + } else { + newGrid(x)[0] = (old[0]*tau/dt+divDgradphi)/(tau/dt-(1.-old[0])*m_phi); + } + // Fully explicit forward-Euler discretization + //newGrid(x)[0] = oldGrid(i)[0] + dt*dphidt/tau; + + // Update undercooling field + T lapT=0; + for (int d=0; d void update(grid& oldGrid, int steps) rank = MPI::COMM_WORLD.Get_rank(); #endif + ghostswap(oldGrid); + grid newGrid(oldGrid); grid temp(oldGrid); diff --git a/examples/statistical_mechanics/Heisenberg/heisenberg.cpp b/examples/statistical_mechanics/Heisenberg/heisenberg.cpp index da49051..1a1d2b7 100644 --- a/examples/statistical_mechanics/Heisenberg/heisenberg.cpp +++ b/examples/statistical_mechanics/Heisenberg/heisenberg.cpp @@ -63,6 +63,8 @@ template void update(grid >& spinGrid, int s rank = MPI::COMM_WORLD.Get_rank(); #endif + ghostswap(spinGrid); + double J = 1.0; double kT = (dim==3)?0.75:0.50; double pi = acos(-1.0); @@ -128,10 +130,13 @@ template void update(grid >& spinGrid, int s // attempt a spin flip double r = double(rand())/double(RAND_MAX); - if (dE<=0.0) spinGrid(p) = s2; - else if (r void update(grid& spinGrid, int steps) rank = MPI::COMM_WORLD.Get_rank(); #endif + ghostswap(spinGrid); + double J = 2.0; double H = 1.0; double kT = (dim==2)?0.50:0.75; @@ -108,10 +110,13 @@ template void update(grid& spinGrid, int steps) // attempt a spin flip double r = double(rand())/double(RAND_MAX); - if (dE<=0.0) spinGrid(p) = spin2; - else if (r void update(grid& spinGrid, int steps) rank = MPI::COMM_WORLD.Get_rank(); #endif + ghostswap(spinGrid); + int Q = 20; double J = 1.0; double kT = (dim==3)?0.75:0.50; @@ -108,10 +110,13 @@ template void update(grid& spinGrid, int steps) // attempt a spin flip double r = double(rand())/double(RAND_MAX); - if (dE<=0.0) spinGrid(p) = spin2; - else if (r Date: Thu, 18 Feb 2016 17:32:38 -0500 Subject: [PATCH 40/83] improved MPI-IO overwrites --- .../anisotropic/Monte_Carlo/graingrowth.cpp | 38 ++++++------- .../isotropic/Monte_Carlo/graingrowth.cpp | 55 +++++++++---------- include/MMSP.grid.hpp | 42 ++++++-------- test/build_examples.sh | 53 +++++++++++------- 4 files changed, 97 insertions(+), 91 deletions(-) diff --git a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp index c0ea4e9..9c6a686 100644 --- a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp +++ b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp @@ -22,18 +22,15 @@ void generate(int dim, const char* filename) if (dim == 1) { GRID1D initGrid(0, 0, 128); - for (int i = 0; i < nodes(initGrid); i++) { - vector x = position(initGrid, i); - if (x[0] < 32) initGrid(i) = 3; - else if (x[0] > 96) initGrid(i) = 2; - else initGrid(i) = 0; - } + for (int i = 0; i < nodes(initGrid); i++) + initGrid(i) = rand() % 100; output(initGrid, filename); } if (dim == 2) { - GRID2D initGrid(0, 0, 128, 0, 128); + int L=256; + GRID2D initGrid(0, 0, L, 0, L); for (int i = 0; i < nodes(initGrid); i++) initGrid(i) = rand() % 20; @@ -49,7 +46,7 @@ void generate(int dim, const char* filename) } } -template bool isOutsideDomain(const grid& mcGrid, const vector& x) +template bool isOutsideDomain(const grid& mcGrid, const vector& x) { bool outside_domain = false; for (int i = 0; i < dim; i++) { @@ -87,7 +84,6 @@ template void update(grid& mcGrid, int steps) vector x (dim, 0); vector x_prim (dim, 0); - int coordinates_of_cell[dim]; int initial_coordinates[dim]; int num_grids_to_flip[( static_cast(pow(2, dim)) )]; @@ -249,14 +245,14 @@ template void update(grid& mcGrid, int steps) } } - bool site_out_of_domain = false; + bool site_outside_domain = false; for (int i = 0; i < dim; i++) { if (x[i] < x0(mcGrid, i) || x[i] >= x1(mcGrid, i)) { - site_out_of_domain = true; + site_outside_domain = true; break;//break from the for int i loop } } - if (site_out_of_domain == true) { + if (site_outside_domain == true) { hh--; continue; //continue the int hh loop } @@ -281,7 +277,8 @@ template void update(grid& mcGrid, int steps) if (!(i == 0 && j == 0)) { r[0] = x[0] + i; r[1] = x[1] + j; - if (r[0] < g0(mcGrid, 0) || r[0] >= g1(mcGrid, 0) || r[1] < g0(mcGrid, 1) || r[1] >= g1(mcGrid, 1) ) // not periodic BC + if (r[0] < g0(mcGrid, 0) || r[0] >= g1(mcGrid, 0) || + r[1] < g0(mcGrid, 1) || r[1] >= g1(mcGrid, 1) ) // not periodic BC continue;// neighbor outside the global boundary, skip it. int spin = mcGrid(r); neighbors.push_back(spin); @@ -298,8 +295,9 @@ template void update(grid& mcGrid, int steps) r[0] = x[0] + i; r[1] = x[1] + j; r[2] = x[2] + k; - if (r[0] < g0(mcGrid, 0) || r[0] >= g1(mcGrid, 0) || r[1] < g0(mcGrid, 1) || r[1] >= g1(mcGrid, 1) || - r[2] < g0(mcGrid, 2) || r[2] >= g1(mcGrid, 2)) // not periodic BC + if (r[0] < g0(mcGrid, 0) || r[0] >= g1(mcGrid, 0) || + r[1] < g0(mcGrid, 1) || r[1] >= g1(mcGrid, 1) || + r[2] < g0(mcGrid, 2) || r[2] >= g1(mcGrid, 2)) // not periodic BC continue;// neighbor outside the global boundary, skip it. int spin = mcGrid(r); neighbors.push_back(spin); @@ -327,7 +325,8 @@ template void update(grid& mcGrid, int steps) if (!(i == 0 && j == 0)) { r[0] = x[0] + i; r[1] = x[1] + j; - if (r[0] < g0(mcGrid, 0) || r[0] >= g1(mcGrid, 0) || r[1] < g0(mcGrid, 1) || r[1] >= g1(mcGrid, 1) ) // not periodic BC + if (r[0] < g0(mcGrid, 0) || r[0] >= g1(mcGrid, 0) || + r[1] < g0(mcGrid, 1) || r[1] >= g1(mcGrid, 1) ) // not periodic BC continue;// neighbor outside the global boundary, skip it. int spin = mcGrid(r); dE += 0.5 * ((spin != spin2) - (spin != spin1)); @@ -343,11 +342,12 @@ template void update(grid& mcGrid, int steps) r[0] = x[0] + i; r[1] = x[1] + j; r[2] = x[2] + k; - if (r[0] < g0(mcGrid, 0) || r[0] >= g1(mcGrid, 0) || r[1] < g0(mcGrid, 1) || r[1] >= g1(mcGrid, 1) || - r[2] < g0(mcGrid, 2) || r[2] >= g1(mcGrid, 2)) // not periodic BC + if (r[0] < g0(mcGrid, 0) || r[0] >= g1(mcGrid, 0) || + r[1] < g0(mcGrid, 1) || r[1] >= g1(mcGrid, 1) || + r[2] < g0(mcGrid, 2) || r[2] >= g1(mcGrid, 2)) // not periodic BC continue;// neighbor outside the global boundary, skip it. int spin = mcGrid(r); - dE += 0.5 * (spin != spin2) - (spin != spin1); + dE += 0.5 * ((spin != spin2) - (spin != spin1)); } } } diff --git a/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp b/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp index 78fa31d..d8e6e3b 100644 --- a/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp +++ b/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp @@ -1,6 +1,6 @@ /* graingrowth.cpp ** Algorithms for 2D and 3D isotropic Monte Carlo grain growth -** Ghost communiation is performed on 1/4 of all boundaries each time. +** Ghost communication is performed on 1/4 of all boundaries each time. ** Parallel algorithm: Wright, Steven A., et al. "Potts-model grain growth simulations: Parallel algorithms and applications." SAND Report (1997): 1925. ** ** Questions/comments to tany3@rpi.edu (Yixuan Tan) @@ -8,10 +8,10 @@ #ifndef GRAINGROWTH_UPDATE #define GRAINGROWTH_UPDATE -#include"MMSP.hpp" #include -#include"graingrowth.hpp" #include +#include"MMSP.hpp" +#include"graingrowth.hpp" namespace MMSP { @@ -35,7 +35,6 @@ void generate(int dim, const char* filename) initGrid(i) = rand() % 20; output(initGrid, filename); - } else if (dim == 3) { GRID3D initGrid(0, 0, 32, 0, 32, 0, 32); @@ -43,7 +42,6 @@ void generate(int dim, const char* filename) initGrid(i) = rand() % 20; output(initGrid, filename); - } } @@ -85,10 +83,9 @@ template void update(grid& mcGrid, int steps) vector x (dim, 0); vector x_prim (dim, 0); - int coordinates_of_cell[dim]; int initial_coordinates[dim]; - int num_of_grids_to_flip[( static_cast(pow(2, dim)) )]; + int num_grids_to_flip[( static_cast(pow(2, dim)) )]; int first_cell_start_coordinates[dim]; for (int kk = 0; kk < dim; kk++) first_cell_start_coordinates[kk] = x0(mcGrid, kk); for (int i = 0; i < dim; i++) { @@ -112,58 +109,57 @@ template void update(grid& mcGrid, int steps) if (dim == 2) { x_prim = x; - if (!isOutsideDomain(mcGrid, x_prim)) num_of_grids_to_flip[0] += 1; + if (!isOutsideDomain(mcGrid, x_prim)) num_grids_to_flip[0] += 1; x_prim = x; x_prim[1] = x[1] + 1; //0,1 - if (!isOutsideDomain(mcGrid, x_prim)) num_of_grids_to_flip[1] += 1; + if (!isOutsideDomain(mcGrid, x_prim)) num_grids_to_flip[1] += 1; x_prim = x; x_prim[0] = x[0] + 1; //1,0 - if (!isOutsideDomain(mcGrid, x_prim)) num_of_grids_to_flip[2] += 1; + if (!isOutsideDomain(mcGrid, x_prim)) num_grids_to_flip[2] += 1; x_prim = x; x_prim[0] = x[0] + 1; x_prim[1] = x[1] + 1; //1,1 - if (!isOutsideDomain(mcGrid, x_prim)) num_of_grids_to_flip[3] += 1; + if (!isOutsideDomain(mcGrid, x_prim)) num_grids_to_flip[3] += 1; } else if (dim == 3) { x_prim = x;//0,0,0 - if (!isOutsideDomain(mcGrid, x_prim)) num_of_grids_to_flip[0] += 1; + if (!isOutsideDomain(mcGrid, x_prim)) num_grids_to_flip[0] += 1; x_prim = x; x_prim[2] = x[2] + 1; //0,0,1 - if (!isOutsideDomain(mcGrid, x_prim)) num_of_grids_to_flip[1] += 1; + if (!isOutsideDomain(mcGrid, x_prim)) num_grids_to_flip[1] += 1; x_prim = x; x_prim[1] = x[1] + 1; //0,1,0 - if (!isOutsideDomain(mcGrid, x_prim)) num_of_grids_to_flip[2] += 1; + if (!isOutsideDomain(mcGrid, x_prim)) num_grids_to_flip[2] += 1; x_prim = x; x_prim[2] = x[2] + 1; x_prim[1] = x[1] + 1; //0,1,1 - if (!isOutsideDomain(mcGrid, x_prim)) num_of_grids_to_flip[3] += 1; + if (!isOutsideDomain(mcGrid, x_prim)) num_grids_to_flip[3] += 1; x_prim = x; x_prim[0] = x[0] + 1; //1,0,0 - if (!isOutsideDomain(mcGrid, x_prim)) num_of_grids_to_flip[4] += 1; + if (!isOutsideDomain(mcGrid, x_prim)) num_grids_to_flip[4] += 1; x_prim = x; x_prim[2] = x[2] + 1; x_prim[0] = x[0] + 1; //1,0,1 - if (!isOutsideDomain(mcGrid, x_prim)) num_of_grids_to_flip[5] += 1; + if (!isOutsideDomain(mcGrid, x_prim)) num_grids_to_flip[5] += 1; x_prim = x; x_prim[1] = x[1] + 1; x_prim[0] = x[0] + 1; //1,1,0 - if (!isOutsideDomain(mcGrid, x_prim)) num_of_grids_to_flip[6] += 1; + if (!isOutsideDomain(mcGrid, x_prim)) num_grids_to_flip[6] += 1; x_prim = x; x_prim[2] = x[2] + 1; x_prim[1] = x[1] + 1; x_prim[0] = x[0] + 1; //1,1,1 - if (!isOutsideDomain(mcGrid, x_prim)) num_of_grids_to_flip[7] += 1; - + if (!isOutsideDomain(mcGrid, x_prim)) num_grids_to_flip[7] += 1; } }// for int j @@ -177,16 +173,16 @@ template void update(grid& mcGrid, int steps) for (int step = 0; step < steps; step++) { if (rank == 0) print_progress(step, steps); - int num_of_sublattices = 0; - if (dim == 2) num_of_sublattices = 4; - else if (dim == 3) num_of_sublattices = 8; - for (int sublattice = 0; sublattice < num_of_sublattices; sublattice++) { + int num_sublattices = 0; + if (dim == 2) num_sublattices = 4; + else if (dim == 3) num_sublattices = 8; + for (int sublattice = 0; sublattice < num_sublattices; sublattice++) { vector x (dim, 0); // srand() is called exactly once in MMSP.main.hpp. Do not call it here. - for (int hh = 0; hh < num_of_grids_to_flip[sublattice]; hh++) { - int cell_numbering = rand() % (num_lattice_cells); //choose a cell to flip, from 0 to num_of_cells_in_thread-1 + for (int hh = 0; hh < num_grids_to_flip[sublattice]; hh++) { + int cell_numbering = rand() % (num_lattice_cells); //choose a cell to flip, from 0 to num_cells_in_thread-1 int cell_coords_selected[dim]; if (dim == 2) { cell_coords_selected[dim - 1] = cell_numbering % lattice_cells_each_dimension[dim - 1]; //1-indexed @@ -244,17 +240,18 @@ template void update(grid& mcGrid, int steps) x[2]++; x[1]++; x[0]++; //1,1,1 + break; } } - bool site_out_of_domain = false; + bool site_outside_domain = false; for (int i = 0; i < dim; i++) { if (x[i] < x0(mcGrid, i) || x[i] >= x1(mcGrid, i)) { - site_out_of_domain = true; + site_outside_domain = true; break;//break from the for int i loop } } - if (site_out_of_domain == true) { + if (site_outside_domain == true) { hh--; continue; //continue the int hh loop } diff --git a/include/MMSP.grid.hpp b/include/MMSP.grid.hpp index bce6389..b8b2a34 100644 --- a/include/MMSP.grid.hpp +++ b/include/MMSP.grid.hpp @@ -1572,19 +1572,16 @@ class grid if (rank==0) std::cout<<"Bug: using normal IO, instead of BGQ IO!"< 0 ]] do key="$1" case $key in - -c|--clean) + --force) + echo -n ", forcing build" + MFLAG="-B" + ;; + --clean) echo -n ", cleaning up after" CLEAN=true ;; - -p|--purge) + --purge) echo -n ", cleaning up after" CLEAN=true PURGE=true ;; - -n|--noexec) + --noexec) echo -n ", not executing" NEXEC=true ;; - -l|--long) + --long) echo -n ", taking 5,000 steps" ITERS=5000 INTER=1000 ;; - -x|--extra) + --extra) echo -n ", taking 25,000 steps" ITERS=25000 INTER=5000 ;; + --noviz) + echo -n ", not converting to PNG" + NOVIZ=true + ;; *) echo "WARNING: Unknown option ${key}." echo @@ -83,6 +86,15 @@ done echo +if [[ ! $NOVIZ ]] +then + if [[ $(which mmsp2png) == "" ]] + then + # Consult doc/MMSP.manual.pdf if this fails. + echo "mmsp2png utility not found. Please check your installation." + fi +fi + exdirs=("coarsening/grain_growth/anisotropic/Monte_Carlo/" \ "coarsening/grain_growth/anisotropic/phase_field/" \ "coarsening/grain_growth/anisotropic/sparsePF/" \ @@ -115,8 +127,8 @@ do cd $examples/${exdirs[$i]} printf "%2d/%2d %-50s\t" $j $n ${exdirs[$i]} echo $(date) >test.log - (make -B >>test.log || exit $?) && ((nSerBld++)) - (make -B parallel >>test.log || exit $?) && ((nParBld++)) + (make $MFLAG >>test.log || exit $?) && ((nSerBld++)) + (make $MFLAG parallel >>test.log || exit $?) && ((nParBld++)) if [[ ! $NEXEC ]] then # Remove existing images first. If the test fails, @@ -126,11 +138,14 @@ do mpirun -np $CORES ./parallel --example 2 test.0000.dat >>test.log \ && mpirun -np $CORES ./parallel test.0000.dat $ITERS $INTER >>test.log \ && ((nParRun++)) - # Show the result - for f in *.dat - do - mmsp2png --zoom $f >>test.log - done + if [[ ! $NOVIZ ]] + then + # Show the result + for f in *.dat + do + mmsp2png --zoom $f >>test.log + done + fi fi # Clean up binaries and images if [[ $CLEAN ]] From 8f2b316705bde9b36ce0bb7084726999e557ec99 Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Thu, 18 Feb 2016 18:19:15 -0500 Subject: [PATCH 41/83] restore srand to MC graingrowth examples: it is required for this algorithm to function --- .../grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp | 3 ++- .../grain_growth/isotropic/Monte_Carlo/graingrowth.cpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp index 9c6a686..bccbb83 100644 --- a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp +++ b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp @@ -180,7 +180,8 @@ template void update(grid& mcGrid, int steps) for (int sublattice = 0; sublattice < num_sublattices; sublattice++) { vector x (dim, 0); - // srand() is called exactly once in MMSP.main.hpp. Do not call it here. + // This particular algorithm requires that srand() be called here. + srand(time(NULL)); // Also, time(NULL)+rank is an INCORRECT seed for this purpose. for (int hh = 0; hh < num_grids_to_flip[sublattice]; hh++) { int cell_numbering = rand() % (num_lattice_cells); //choose a cell to flip, from 0 to num_cells_in_thread-1 diff --git a/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp b/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp index d8e6e3b..84e9404 100644 --- a/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp +++ b/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp @@ -179,7 +179,8 @@ template void update(grid& mcGrid, int steps) for (int sublattice = 0; sublattice < num_sublattices; sublattice++) { vector x (dim, 0); - // srand() is called exactly once in MMSP.main.hpp. Do not call it here. + // This particular algorithm requires that srand() be called here. + srand(time(NULL)); // Also, time(NULL)+rank is an INCORRECT seed for this purpose. for (int hh = 0; hh < num_grids_to_flip[sublattice]; hh++) { int cell_numbering = rand() % (num_lattice_cells); //choose a cell to flip, from 0 to num_cells_in_thread-1 From ef8378af700806c2f3a2272f168e265a7ebebacd Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Thu, 18 Feb 2016 19:40:12 -0500 Subject: [PATCH 42/83] MPI specializations (srand for MC, C++11 warning for convex splitting) --- .../grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp | 8 ++++++-- .../grain_growth/isotropic/Monte_Carlo/graingrowth.cpp | 8 ++++++-- .../cahn-hilliard/convex_splitting/Makefile | 4 ++-- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp index bccbb83..4d7df98 100644 --- a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp +++ b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp @@ -29,7 +29,7 @@ void generate(int dim, const char* filename) } if (dim == 2) { - int L=256; + int L=64; GRID2D initGrid(0, 0, L, 0, L); for (int i = 0; i < nodes(initGrid); i++) @@ -181,7 +181,11 @@ template void update(grid& mcGrid, int steps) vector x (dim, 0); // This particular algorithm requires that srand() be called here. - srand(time(NULL)); // Also, time(NULL)+rank is an INCORRECT seed for this purpose. + unsigned long seed=time(NULL); + #ifdef MPI_VERSION + MPI::COMM_WORLD.Bcast(&seed, 1, MPI_UNSIGNED_LONG, 0); + #endif + srand(seed); // Also, time(NULL)+rank is an INCORRECT seed for this purpose. for (int hh = 0; hh < num_grids_to_flip[sublattice]; hh++) { int cell_numbering = rand() % (num_lattice_cells); //choose a cell to flip, from 0 to num_cells_in_thread-1 diff --git a/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp b/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp index 84e9404..d30a7c3 100644 --- a/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp +++ b/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp @@ -28,7 +28,7 @@ void generate(int dim, const char* filename) } if (dim == 2) { - int L=256; + int L=64; GRID2D initGrid(0, 0, L, 0, L); for (int i = 0; i < nodes(initGrid); i++) @@ -180,7 +180,11 @@ template void update(grid& mcGrid, int steps) vector x (dim, 0); // This particular algorithm requires that srand() be called here. - srand(time(NULL)); // Also, time(NULL)+rank is an INCORRECT seed for this purpose. + unsigned long seed=time(NULL); + #ifdef MPI_VERSION + MPI::COMM_WORLD.Bcast(&seed, 1, MPI_UNSIGNED_LONG, 0); + #endif + srand(seed); // Also, time(NULL)+rank is an INCORRECT seed for this purpose. for (int hh = 0; hh < num_grids_to_flip[sublattice]; hh++) { int cell_numbering = rand() % (num_lattice_cells); //choose a cell to flip, from 0 to num_cells_in_thread-1 diff --git a/examples/phase_transitions/cahn-hilliard/convex_splitting/Makefile b/examples/phase_transitions/cahn-hilliard/convex_splitting/Makefile index ee4abb3..c1c8841 100644 --- a/examples/phase_transitions/cahn-hilliard/convex_splitting/Makefile +++ b/examples/phase_transitions/cahn-hilliard/convex_splitting/Makefile @@ -8,8 +8,8 @@ incdir = ../../../../include # compilers/flags compiler = g++ -flags = -O3 -DVANILLA -Wall -std=c++11 -I $(incdir) -gflags = -pg -O2 -DVANILLA -Wall -std=c++11 -I $(incdir) +flags = -O3 -DVANILLA -Wall -Wnoliteral-suffix -std=c++11 -I $(incdir) +gflags = -pg -O2 -DVANILLA -Wall -Wnoliteral-suffix -std=c++11 -I $(incdir) pcompiler = mpic++ pflags = $(flags) -include mpi.h From 9b1f5a73ff5dde15345cd86909730f0be3ede1f0 Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Thu, 18 Feb 2016 19:40:12 -0500 Subject: [PATCH 43/83] MPI specializations (srand for MC, C++11 warning for convex splitting) --- .../grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp | 8 ++++++-- .../grain_growth/isotropic/Monte_Carlo/graingrowth.cpp | 8 ++++++-- .../cahn-hilliard/convex_splitting/Makefile | 2 +- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp index bccbb83..4d7df98 100644 --- a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp +++ b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp @@ -29,7 +29,7 @@ void generate(int dim, const char* filename) } if (dim == 2) { - int L=256; + int L=64; GRID2D initGrid(0, 0, L, 0, L); for (int i = 0; i < nodes(initGrid); i++) @@ -181,7 +181,11 @@ template void update(grid& mcGrid, int steps) vector x (dim, 0); // This particular algorithm requires that srand() be called here. - srand(time(NULL)); // Also, time(NULL)+rank is an INCORRECT seed for this purpose. + unsigned long seed=time(NULL); + #ifdef MPI_VERSION + MPI::COMM_WORLD.Bcast(&seed, 1, MPI_UNSIGNED_LONG, 0); + #endif + srand(seed); // Also, time(NULL)+rank is an INCORRECT seed for this purpose. for (int hh = 0; hh < num_grids_to_flip[sublattice]; hh++) { int cell_numbering = rand() % (num_lattice_cells); //choose a cell to flip, from 0 to num_cells_in_thread-1 diff --git a/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp b/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp index 84e9404..d30a7c3 100644 --- a/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp +++ b/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp @@ -28,7 +28,7 @@ void generate(int dim, const char* filename) } if (dim == 2) { - int L=256; + int L=64; GRID2D initGrid(0, 0, L, 0, L); for (int i = 0; i < nodes(initGrid); i++) @@ -180,7 +180,11 @@ template void update(grid& mcGrid, int steps) vector x (dim, 0); // This particular algorithm requires that srand() be called here. - srand(time(NULL)); // Also, time(NULL)+rank is an INCORRECT seed for this purpose. + unsigned long seed=time(NULL); + #ifdef MPI_VERSION + MPI::COMM_WORLD.Bcast(&seed, 1, MPI_UNSIGNED_LONG, 0); + #endif + srand(seed); // Also, time(NULL)+rank is an INCORRECT seed for this purpose. for (int hh = 0; hh < num_grids_to_flip[sublattice]; hh++) { int cell_numbering = rand() % (num_lattice_cells); //choose a cell to flip, from 0 to num_cells_in_thread-1 diff --git a/examples/phase_transitions/cahn-hilliard/convex_splitting/Makefile b/examples/phase_transitions/cahn-hilliard/convex_splitting/Makefile index ee4abb3..49ef92a 100644 --- a/examples/phase_transitions/cahn-hilliard/convex_splitting/Makefile +++ b/examples/phase_transitions/cahn-hilliard/convex_splitting/Makefile @@ -8,7 +8,7 @@ incdir = ../../../../include # compilers/flags compiler = g++ -flags = -O3 -DVANILLA -Wall -std=c++11 -I $(incdir) +flags = -O3 -DVANILLA -std=c++11 -I $(incdir) gflags = -pg -O2 -DVANILLA -Wall -std=c++11 -I $(incdir) pcompiler = mpic++ pflags = $(flags) -include mpi.h From 80c3a7fb140eb7ce3f570dfed428b6858e395a93 Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Thu, 18 Feb 2016 21:18:53 -0500 Subject: [PATCH 44/83] travis-ci testing --- .travis.yml | 24 +++++++++++++++++++ .../isotropic/sparsePF/zener.cpp | 2 +- .../convex_splitting/cahn-hilliard.cpp | 2 +- test/travis-mpi.sh | 13 ++++++++++ 4 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 .travis.yml create mode 100755 test/travis-mpi.sh diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..327c026 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,24 @@ +language: cpp + +os: + - linux + +compiler: + - gcc + +env: + - MPI=mpich2 + - MPI=openmpi + +before_install: + - test -n $CC && unset CC + - test -n $CXX && unset CXX + +install: + - sudo apt-get update -qq -y + -sh test/travis-mpi.sh + +before_script: + - cd test + +script: ./build_examples --noviz --clean diff --git a/examples/coarsening/zener_pinning/isotropic/sparsePF/zener.cpp b/examples/coarsening/zener_pinning/isotropic/sparsePF/zener.cpp index db3f3f0..5767349 100644 --- a/examples/coarsening/zener_pinning/isotropic/sparsePF/zener.cpp +++ b/examples/coarsening/zener_pinning/isotropic/sparsePF/zener.cpp @@ -38,7 +38,7 @@ void generate(int dim, const char* filename) } if (dim==2) { - int L=256; + int L=128; GRID2D initGrid(0,0,L,0,L); for (int i=0; i Date: Thu, 18 Feb 2016 21:21:53 -0500 Subject: [PATCH 45/83] sensitive to syntax --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 327c026..59ef9c6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,7 +16,7 @@ before_install: install: - sudo apt-get update -qq -y - -sh test/travis-mpi.sh + - sh test/travis-mpi.sh before_script: - cd test From ebacb0569babb48313460a3900542c7fe434d58a Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Thu, 18 Feb 2016 21:27:23 -0500 Subject: [PATCH 46/83] forgot the MPI option --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 59ef9c6..d402905 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,9 +16,10 @@ before_install: install: - sudo apt-get update -qq -y - - sh test/travis-mpi.sh + - sh test/travis-mpi.sh $MPI before_script: + - test $MPI == mpich2 && MPIEXEC='mpiexec -launcher fork' || true - cd test script: ./build_examples --noviz --clean From 7fac1879506e34a578a0a3f76b3f03344ad4d533 Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Thu, 18 Feb 2016 21:30:50 -0500 Subject: [PATCH 47/83] should really remember filenames better --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index d402905..2866c66 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,11 +15,10 @@ before_install: - test -n $CXX && unset CXX install: - - sudo apt-get update -qq -y - sh test/travis-mpi.sh $MPI before_script: - test $MPI == mpich2 && MPIEXEC='mpiexec -launcher fork' || true - cd test -script: ./build_examples --noviz --clean +script: ./build_examples.sh --noviz --clean From e5d6e8d6c4f44784562fa08f41dcee52d7d62a8f Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Thu, 18 Feb 2016 21:33:22 -0500 Subject: [PATCH 48/83] travis options --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index 2866c66..6ca72b4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,11 +10,15 @@ env: - MPI=mpich2 - MPI=openmpi +notifications: + email: false + before_install: - test -n $CC && unset CC - test -n $CXX && unset CXX install: + - sudo apt-get update -qq -y - sh test/travis-mpi.sh $MPI before_script: From 97f037aa7b2b611aad8ff1cd722ed5b83b35ffba Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Thu, 18 Feb 2016 21:48:20 -0500 Subject: [PATCH 49/83] travis has 2 cores and a 15-minute runtime limit --- .travis.yml | 3 +-- README.md | 1 + .../cahn-hilliard/convex_splitting/cahn-hilliard.cpp | 8 ++++---- test/build_examples.sh | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6ca72b4..b43b730 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,7 +22,6 @@ install: - sh test/travis-mpi.sh $MPI before_script: - - test $MPI == mpich2 && MPIEXEC='mpiexec -launcher fork' || true - cd test -script: ./build_examples.sh --noviz --clean +script: ./build_examples.sh --noexec --noviz && ./build_examples.sh --noviz --clean diff --git a/README.md b/README.md index ee6f2b0..10690fb 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ Mesoscale Microstructure Simulation Project ==== +[![Build Status](https://travis-ci.org/tkphd/mmsp.svg?branch=master)](https://travis-ci.org/tkphd/mmsp) The goal of the Mesoscale Microstructure Simulation Project (MMSP) is to provide a simple, consistent, and extensible programming interface for all grid and mesh based microstructure diff --git a/examples/phase_transitions/cahn-hilliard/convex_splitting/cahn-hilliard.cpp b/examples/phase_transitions/cahn-hilliard/convex_splitting/cahn-hilliard.cpp index 55b12db..20c37bf 100644 --- a/examples/phase_transitions/cahn-hilliard/convex_splitting/cahn-hilliard.cpp +++ b/examples/phase_transitions/cahn-hilliard/convex_splitting/cahn-hilliard.cpp @@ -32,15 +32,15 @@ ** flags = -O3 -Wall ... **/ -// Spatial constants -int edge = 128; -double deltaX = 1.0; - #ifdef VANILLA +int edge = 64; +double deltaX = 1.0; double C0 = 0.0; // system composition double D = 1.0; // diffusivity double K = 1.0; // gradient energy coefficient #else +int edge = 200; +double deltaX = 1.0; double C0 = 0.45; // mean composition double Ca = 0.05; // alpha-phase solvus composition double Cb = 0.95; // beta-phase solvus composition diff --git a/test/build_examples.sh b/test/build_examples.sh index f463849..ffbfa10 100755 --- a/test/build_examples.sh +++ b/test/build_examples.sh @@ -30,9 +30,9 @@ nParBld=0 nParRun=0 # Set execution parameters -ITERS=1000 -INTER=500 -CORES=4 +ITERS=100 +INTER=100 +CORES=2 # Get going cd ../examples From 713cd4edeca07bf1345076def3a7c4999e7c47cc Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Thu, 18 Feb 2016 22:07:03 -0500 Subject: [PATCH 50/83] travis does not support cpp11 by default --- .travis.yml | 10 ++++++++-- test/build_examples.sh | 17 ++++++++++++----- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index b43b730..4fef30c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,9 +13,15 @@ env: notifications: email: false +addons: + sources: + - ubuntu-toolchain-r-test + packages: + - gcc-4.8 + - g++-4.8 + before_install: - - test -n $CC && unset CC - - test -n $CXX && unset CXX + export CXX="g++-4.8" CC="gcc-4.8" install: - sudo apt-get update -qq -y diff --git a/test/build_examples.sh b/test/build_examples.sh index ffbfa10..3d0542b 100755 --- a/test/build_examples.sh +++ b/test/build_examples.sh @@ -25,6 +25,7 @@ # Initialize timer and completion counters tstart=$(date +%s) +err=0 nSerBld=0 nParBld=0 nParRun=0 @@ -127,16 +128,16 @@ do cd $examples/${exdirs[$i]} printf "%2d/%2d %-50s\t" $j $n ${exdirs[$i]} echo $(date) >test.log - (make $MFLAG >>test.log || exit $?) && ((nSerBld++)) - (make $MFLAG parallel >>test.log || exit $?) && ((nParBld++)) + (make $MFLAG >>test.log || ((err++))) && ((nSerBld++)) + (make $MFLAG parallel >>test.log || ((err++))) && ((nParBld++)) if [[ ! $NEXEC ]] then # Remove existing images first. If the test fails, # no images in the directory is an obvious red flag. rm -f test.*.png # Run the example in parallel, for speed. - mpirun -np $CORES ./parallel --example 2 test.0000.dat >>test.log \ - && mpirun -np $CORES ./parallel test.0000.dat $ITERS $INTER >>test.log \ + (mpirun -np $CORES ./parallel --example 2 test.0000.dat >>test.log || ((err++))) \ + && (mpirun -np $CORES ./parallel test.0000.dat $ITERS $INTER >>test.log || ((err++))) \ && ((nParRun++)) if [[ ! $NOVIZ ]] then @@ -166,6 +167,7 @@ done cd ${examples} echo +echo "${elapsed} seconds elapsed." echo "${nSerBld} serial examples built successfully." echo "${nParBld} parallel examples built successfully." echo "${nParRun} parallel examples executed successfully." @@ -173,6 +175,11 @@ echo "${nParRun} parallel examples executed successfully." tfinish=$(date +%s) elapsed=$(echo "$tfinish-$tstart" | bc -l) -echo "${elapsed} seconds elapsed." cd ../test/ + +if [[ $err>0 ]] +then + echo "${err} tests failed." + exit 1 +fi From 1940ad1883ee784e6750281283f9a7b84bd474d2 Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Thu, 18 Feb 2016 22:08:18 -0500 Subject: [PATCH 51/83] spacing out --- .travis.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4fef30c..af58f37 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,8 +17,8 @@ addons: sources: - ubuntu-toolchain-r-test packages: - - gcc-4.8 - - g++-4.8 + - gcc-4.8 + - g++-4.8 before_install: export CXX="g++-4.8" CC="gcc-4.8" @@ -30,4 +30,5 @@ install: before_script: - cd test -script: ./build_examples.sh --noexec --noviz && ./build_examples.sh --noviz --clean +script: + ./build_examples.sh --noexec --noviz && ./build_examples.sh --noviz --clean From 150374eda1a77907238360e74171359427b7d0af Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Thu, 18 Feb 2016 22:19:38 -0500 Subject: [PATCH 52/83] still trying to get cpp11 --- .travis.yml | 1 + test/build_examples.sh | 25 +++++++++++++------------ test/travis-alias.sh | 2 ++ 3 files changed, 16 insertions(+), 12 deletions(-) create mode 100755 test/travis-alias.sh diff --git a/.travis.yml b/.travis.yml index af58f37..94e0b15 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,6 +26,7 @@ before_install: install: - sudo apt-get update -qq -y - sh test/travis-mpi.sh $MPI + - source test/travis-alias.sh before_script: - cd test diff --git a/test/build_examples.sh b/test/build_examples.sh index 3d0542b..7e6a60e 100755 --- a/test/build_examples.sh +++ b/test/build_examples.sh @@ -25,7 +25,7 @@ # Initialize timer and completion counters tstart=$(date +%s) -err=0 +ERR=0 nSerBld=0 nParBld=0 nParRun=0 @@ -128,16 +128,16 @@ do cd $examples/${exdirs[$i]} printf "%2d/%2d %-50s\t" $j $n ${exdirs[$i]} echo $(date) >test.log - (make $MFLAG >>test.log || ((err++))) && ((nSerBld++)) - (make $MFLAG parallel >>test.log || ((err++))) && ((nParBld++)) + (make $MFLAG >>test.log || ((ERR++))) && ((nSerBld++)) + (make $MFLAG parallel >>test.log || ((ERR++))) && ((nParBld++)) if [[ ! $NEXEC ]] then # Remove existing images first. If the test fails, # no images in the directory is an obvious red flag. rm -f test.*.png # Run the example in parallel, for speed. - (mpirun -np $CORES ./parallel --example 2 test.0000.dat >>test.log || ((err++))) \ - && (mpirun -np $CORES ./parallel test.0000.dat $ITERS $INTER >>test.log || ((err++))) \ + (mpirun -np $CORES ./parallel --example 2 test.0000.dat >>test.log || ((ERR++))) \ + && (mpirun -np $CORES ./parallel test.0000.dat $ITERS $INTER >>test.log || ((ERR++))) \ && ((nParRun++)) if [[ ! $NOVIZ ]] then @@ -164,22 +164,23 @@ do exlapse=$(echo "$exfin-$exstart" | bc -l) echo "${exlapse} seconds" done + cd ${examples} +tfinish=$(date +%s) +elapsed=$(echo "$tfinish-$tstart" | bc -l) + echo echo "${elapsed} seconds elapsed." echo "${nSerBld} serial examples built successfully." echo "${nParBld} parallel examples built successfully." echo "${nParRun} parallel examples executed successfully." - -tfinish=$(date +%s) -elapsed=$(echo "$tfinish-$tstart" | bc -l) - - +echo cd ../test/ -if [[ $err>0 ]] +if [[ $ERR>0 ]] then - echo "${err} tests failed." + echo "${ERR} tests failed." + echo exit 1 fi diff --git a/test/travis-alias.sh b/test/travis-alias.sh new file mode 100755 index 0000000..53a4dba --- /dev/null +++ b/test/travis-alias.sh @@ -0,0 +1,2 @@ +alias g++="g++-4.8" +alias gcc="gcc-4.8" From 2460650488fa45fc6197de33322bdea375ffa4bf Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Fri, 19 Feb 2016 00:05:26 -0500 Subject: [PATCH 53/83] error detection --- .travis.yml | 17 +++---------- .../anisotropic/Monte_Carlo/Makefile | 2 +- .../anisotropic/Monte_Carlo/graingrowth.cpp | 3 --- .../anisotropic/Monte_Carlo/graingrowth.hpp | 6 ++--- .../cahn-hilliard/convex_splitting/Makefile | 4 +-- .../convex_splitting/cahn-hilliard.cpp | 9 +------ test/build_examples.sh | 25 +++++++++++-------- test/travis-alias.sh | 2 -- 8 files changed, 24 insertions(+), 44 deletions(-) delete mode 100755 test/travis-alias.sh diff --git a/.travis.yml b/.travis.yml index 94e0b15..e42e7f9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,6 @@ language: cpp - -os: - - linux +dist: trusty +sudo: required compiler: - gcc @@ -13,23 +12,13 @@ env: notifications: email: false -addons: - sources: - - ubuntu-toolchain-r-test - packages: - - gcc-4.8 - - g++-4.8 - -before_install: - export CXX="g++-4.8" CC="gcc-4.8" - install: - sudo apt-get update -qq -y - sh test/travis-mpi.sh $MPI - - source test/travis-alias.sh before_script: - cd test + - nproc script: ./build_examples.sh --noexec --noviz && ./build_examples.sh --noviz --clean diff --git a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/Makefile b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/Makefile index 723a2a7..0e98487 100644 --- a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/Makefile +++ b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/Makefile @@ -9,7 +9,7 @@ incdir = ../../../../../include compiler = g++ flags = -O3 -I $(incdir) pcompiler = mpic++ -pflags = -O3 -I $(incdir) -include mpi.h +pflags = $(flags) -include mpi.h # dependencies core = $(incdir)/MMSP.main.hpp \ diff --git a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp index 4d7df98..281ab3b 100644 --- a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp +++ b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp @@ -61,11 +61,8 @@ template bool isOutsideDomain(const grid& mcGrid, const vecto template void update(grid& mcGrid, int steps) { int rank = 0; - unsigned int np = 0; #ifdef MPI_VERSION rank = MPI::COMM_WORLD.Get_rank(); - np = MPI::COMM_WORLD.Get_size(); - MPI::COMM_WORLD.Barrier(); #endif ghostswap(mcGrid); diff --git a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.hpp b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.hpp index d6add1d..903fe47 100644 --- a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.hpp +++ b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.hpp @@ -6,7 +6,7 @@ std::string PROGRAM = "graingrowth"; std::string MESSAGE = "Monte Carlo grain growth code"; -typedef MMSP::grid<1, int> GRID1D; -typedef MMSP::grid<2, int> GRID2D; -typedef MMSP::grid<3, int> GRID3D; +typedef MMSP::grid<1,int> GRID1D; +typedef MMSP::grid<2,int> GRID2D; +typedef MMSP::grid<3,int> GRID3D; diff --git a/examples/phase_transitions/cahn-hilliard/convex_splitting/Makefile b/examples/phase_transitions/cahn-hilliard/convex_splitting/Makefile index 49ef92a..5467cdb 100644 --- a/examples/phase_transitions/cahn-hilliard/convex_splitting/Makefile +++ b/examples/phase_transitions/cahn-hilliard/convex_splitting/Makefile @@ -8,8 +8,8 @@ incdir = ../../../../include # compilers/flags compiler = g++ -flags = -O3 -DVANILLA -std=c++11 -I $(incdir) -gflags = -pg -O2 -DVANILLA -Wall -std=c++11 -I $(incdir) +flags = -O3 -DVANILLA -I $(incdir) +gflags = -pg -O2 -DVANILLA -Wall -I $(incdir) pcompiler = mpic++ pflags = $(flags) -include mpi.h diff --git a/examples/phase_transitions/cahn-hilliard/convex_splitting/cahn-hilliard.cpp b/examples/phase_transitions/cahn-hilliard/convex_splitting/cahn-hilliard.cpp index 20c37bf..bdc72b9 100644 --- a/examples/phase_transitions/cahn-hilliard/convex_splitting/cahn-hilliard.cpp +++ b/examples/phase_transitions/cahn-hilliard/convex_splitting/cahn-hilliard.cpp @@ -16,7 +16,6 @@ #include #include #include -#include #include"cahn-hilliard.hpp" /** This code sets parameters for the evolution of two different energy configurations. @@ -192,12 +191,6 @@ void generate(int dim, const char* filename) rank = MPI::COMM_WORLD.Get_rank(); #endif - #ifdef VANILLA - // Call the Mersenne Twister. ***NOTE: Requires C++11 (or a compatible MT19937_64 include)*** - std::mt19937_64 mt_rand(time(NULL)+rank); - std::uniform_real_distribution real_gen(0,1); - #endif - if (dim==2) { grid<2,vector > initGrid(2,0,edge,0,edge); // field 0 is c, field 1 is mu for (int d=0; dtest.log - (make $MFLAG >>test.log || ((ERR++))) && ((nSerBld++)) - (make $MFLAG parallel >>test.log || ((ERR++))) && ((nParBld++)) + make $MFLAG >>test.log && ((nSerBld++)) || (((SerERR++)) && tail test.log) + make $MFLAG parallel >>test.log && ((nParBld++)) || (((ParERR++)) && tail test.log) if [[ ! $NEXEC ]] then # Remove existing images first. If the test fails, # no images in the directory is an obvious red flag. rm -f test.*.png # Run the example in parallel, for speed. - (mpirun -np $CORES ./parallel --example 2 test.0000.dat >>test.log || ((ERR++))) \ - && (mpirun -np $CORES ./parallel test.0000.dat $ITERS $INTER >>test.log || ((ERR++))) \ - && ((nParRun++)) + mpirun -np $CORES ./parallel --example 2 test.0000.dat >>test.log && \ + mpirun -np $CORES ./parallel test.0000.dat $ITERS $INTER >>test.log && \ + ((nParRun++)) || (((RunERR++)) && tail test.log) if [[ ! $NOVIZ ]] then # Show the result @@ -172,15 +174,16 @@ elapsed=$(echo "$tfinish-$tstart" | bc -l) echo echo "${elapsed} seconds elapsed." -echo "${nSerBld} serial examples built successfully." -echo "${nParBld} parallel examples built successfully." -echo "${nParRun} parallel examples executed successfully." +echo "${nSerBld} serial examples built successfully, ${SerERR} failed." +echo "${nParBld} parallel examples built successfully, ${ParERR} failed." +echo "${nParRun} parallel examples executed successfully, ${RunERR} failed." echo cd ../test/ -if [[ $ERR>0 ]] +AllERR=$(echo "$SerERR+$ParERR+$RunERR" | bc -l) +if [[ $AllERR > 0 ]] then - echo "${ERR} tests failed." + echo "${AllERR} tests failed." echo exit 1 fi diff --git a/test/travis-alias.sh b/test/travis-alias.sh deleted file mode 100755 index 53a4dba..0000000 --- a/test/travis-alias.sh +++ /dev/null @@ -1,2 +0,0 @@ -alias g++="g++-4.8" -alias gcc="gcc-4.8" From 1b1454bf84746b378c7d256bb431087b85c30836 Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Fri, 19 Feb 2016 00:08:22 -0500 Subject: [PATCH 54/83] package mgmt --- test/travis-mpi.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/travis-mpi.sh b/test/travis-mpi.sh index c4cf6a1..e9881f4 100755 --- a/test/travis-mpi.sh +++ b/test/travis-mpi.sh @@ -5,7 +5,7 @@ set -e case $1 in mpich2) set -x; - sudo apt-get install -q mpich2 libmpich2-3 libmpich2-dev;; + sudo apt-get install -q mpich2 libmpich2-dev;; openmpi) set -x; sudo apt-get install openmpi-bin openmpi-dev;; *) From 7795e359b6e8879bb16bec269403b5b8596e7516 Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Fri, 19 Feb 2016 00:36:38 -0500 Subject: [PATCH 55/83] more pkg mgmt --- test/travis-mpi.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/travis-mpi.sh b/test/travis-mpi.sh index e9881f4..c114576 100755 --- a/test/travis-mpi.sh +++ b/test/travis-mpi.sh @@ -5,9 +5,9 @@ set -e case $1 in mpich2) set -x; - sudo apt-get install -q mpich2 libmpich2-dev;; + sudo apt-get -y install -qq mpich2 libmpich2-dev;; openmpi) set -x; - sudo apt-get install openmpi-bin openmpi-dev;; + sudo apt-get -y install -qq openmpi-bin openmpi-dev;; *) echo "Unknown MPI implementation:" $1; exit 1;; esac From 76ec705e152a4ccfa2bc5ae8d031ac8073e8a1b8 Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Fri, 19 Feb 2016 00:41:39 -0500 Subject: [PATCH 56/83] MPI pkg mgmt --- test/travis-mpi.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/travis-mpi.sh b/test/travis-mpi.sh index c114576..81389de 100755 --- a/test/travis-mpi.sh +++ b/test/travis-mpi.sh @@ -7,7 +7,7 @@ case $1 in mpich2) set -x; sudo apt-get -y install -qq mpich2 libmpich2-dev;; openmpi) set -x; - sudo apt-get -y install -qq openmpi-bin openmpi-dev;; + sudo apt-get -y install -qq openmpi-bin libopenmpi-dev;; *) echo "Unknown MPI implementation:" $1; exit 1;; esac From b743319b70da119f2b87875bed2d1a874af4b36c Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Fri, 19 Feb 2016 00:43:56 -0500 Subject: [PATCH 57/83] longer tests --- test/build_examples.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/build_examples.sh b/test/build_examples.sh index 713fd97..a53c069 100755 --- a/test/build_examples.sh +++ b/test/build_examples.sh @@ -33,8 +33,8 @@ nParBld=0 nParRun=0 # Set execution parameters -ITERS=100 -INTER=100 +ITERS=1000 +INTER=500 CORES=2 # Get going From a01bf6bc292e8c6a52acbfee95d284f7dd88775b Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Fri, 19 Feb 2016 15:15:51 -0500 Subject: [PATCH 58/83] tune Makefiles * Explicit dependency declarations are not necessary, and including $(core) is superfluous. #include"MMSP.hpp" pulls in what it needs to. * Monte Carlo grain growth examples exhibit symptoms of a subtle memory bug (see Issue #21). Compiler optimizations are reduced to "safe" levels. --- examples/beginners_diffusion/Makefile | 8 +++--- .../anisotropic/Monte_Carlo/Makefile | 18 ++++++------ .../anisotropic/phase_field/Makefile | 14 +++------- .../anisotropic/sparsePF/Makefile | 14 +++------- .../isotropic/Monte_Carlo/Makefile | 14 +++------- .../isotropic/phase_field/Makefile | 14 +++------- .../grain_growth/isotropic/sparsePF/Makefile | 14 +++------- .../isotropic/phase_field/Makefile | 14 +++------- .../anisotropic/Monte_Carlo/Makefile | 14 +++------- .../anisotropic/phase_field/Makefile | 14 +++------- .../anisotropic/sparsePF/Makefile | 14 +++------- .../isotropic/Monte_Carlo/Makefile | 14 +++------- .../isotropic/phase_field/Makefile | 14 +++------- .../zener_pinning/isotropic/sparsePF/Makefile | 14 +++------- .../elliptic/Poisson/Makefile | 13 +++------ .../phase_transitions/allen-cahn/Makefile | 13 +++------ .../cahn-hilliard/convex_splitting/Makefile | 13 +++------ .../cahn-hilliard/explicit/Makefile | 13 +++------ examples/phase_transitions/model_A/Makefile | 13 +++------ examples/phase_transitions/model_B/Makefile | 13 +++------ .../solidification/anisotropic/Makefile | 11 ++------ examples/phase_transitions/spinodal/Makefile | 13 +++------ .../statistical_mechanics/Heisenberg/Makefile | 14 +++------- examples/statistical_mechanics/Ising/Makefile | 13 +++------ examples/statistical_mechanics/Potts/Makefile | 13 +++------ test/build_examples.sh | 28 ++++++++++++++----- 26 files changed, 123 insertions(+), 241 deletions(-) diff --git a/examples/beginners_diffusion/Makefile b/examples/beginners_diffusion/Makefile index 114ed13..b0b7d53 100644 --- a/examples/beginners_diffusion/Makefile +++ b/examples/beginners_diffusion/Makefile @@ -5,7 +5,7 @@ CCOPTS = -g PGOPTS = LIBS = -lm -core = ../../include +incdir = ../../include # If you have gnuplot pipes on your system, change this # value to 'yes' and change the options in the source @@ -51,13 +51,13 @@ MMSPDiffusion2D : $(OBJECT2) # Object defs below here. 1stDiffusion.o : 1stDiffusion.cpp - $(CC) $(CCOPTIONS) -I$(core) -c $< + $(CC) $(CCOPTIONS) -I$(incdir) -c $< MMSPDiffusion.o : MMSPDiffusion.cpp - $(CC) $(CCOPTIONS) -I$(core) -c $< + $(CC) $(CCOPTIONS) -I$(incdir) -c $< MMSPDiffusion2D.o : MMSPDiffusion2D.cpp - $(CC) $(CCOPTIONS) -I$(core) -c $< + $(CC) $(CCOPTIONS) -I$(incdir) -c $< gnuplot_i.hpp.gch : gnuplot_i.hpp $(CC) $(CCOPTIONS) -c gnuplot_i.hpp diff --git a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/Makefile b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/Makefile index 0e98487..4ad5f81 100644 --- a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/Makefile +++ b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/Makefile @@ -7,22 +7,20 @@ incdir = ../../../../../include # compilers/flags compiler = g++ -flags = -O3 -I $(incdir) pcompiler = mpic++ +flags = -O1 -I $(incdir) +gflags = -g -I $(incdir) pflags = $(flags) -include mpi.h -# dependencies -core = $(incdir)/MMSP.main.hpp \ - $(incdir)/MMSP.utility.hpp \ - $(incdir)/MMSP.grid.hpp \ - $(incdir)/MMSP.vector.hpp - # the program -graingrowth: graingrowth.cpp $(core) +graingrowth: graingrowth.cpp $(compiler) $(flags) $< -o $@ -lz -parallel: graingrowth.cpp $(core) +debug: graingrowth.cpp + $(compiler) $(gflags) $< -o $@ -lz + +parallel: graingrowth.cpp $(pcompiler) $(pflags) $< -o $@ -lz clean: - rm -f graingrowth parallel + rm -f graingrowth debug parallel diff --git a/examples/coarsening/grain_growth/anisotropic/phase_field/Makefile b/examples/coarsening/grain_growth/anisotropic/phase_field/Makefile index 7c084c5..97baf9e 100644 --- a/examples/coarsening/grain_growth/anisotropic/phase_field/Makefile +++ b/examples/coarsening/grain_growth/anisotropic/phase_field/Makefile @@ -7,21 +7,15 @@ incdir = ../../../../../include # compilers/flags compiler = g++ -flags = -O3 -I $(incdir) pcompiler = mpic++ -pflags = -O3 -I $(incdir) -include mpi.h - -# dependencies -core = $(incdir)/MMSP.main.hpp \ - $(incdir)/MMSP.utility.hpp \ - $(incdir)/MMSP.grid.hpp \ - $(incdir)/MMSP.vector.hpp +flags = -O3 -I $(incdir) +pflags = $(flags) -include mpi.h # the program -graingrowth: graingrowth.cpp anisotropy.hpp $(core) +graingrowth: graingrowth.cpp $(compiler) $(flags) $< -o $@ -lz -parallel: graingrowth.cpp anisotropy.hpp $(core) +parallel: graingrowth.cpp $(pcompiler) $(pflags) $< -o $@ -lz clean: diff --git a/examples/coarsening/grain_growth/anisotropic/sparsePF/Makefile b/examples/coarsening/grain_growth/anisotropic/sparsePF/Makefile index 0a59192..97baf9e 100644 --- a/examples/coarsening/grain_growth/anisotropic/sparsePF/Makefile +++ b/examples/coarsening/grain_growth/anisotropic/sparsePF/Makefile @@ -7,21 +7,15 @@ incdir = ../../../../../include # compilers/flags compiler = g++ -flags = -O3 -I $(incdir) pcompiler = mpic++ -pflags = -O3 -I $(incdir) -include mpi.h - -# dependencies -core = $(incdir)/MMSP.main.hpp \ - $(incdir)/MMSP.utility.hpp \ - $(incdir)/MMSP.grid.hpp \ - $(incdir)/MMSP.sparse.hpp +flags = -O3 -I $(incdir) +pflags = $(flags) -include mpi.h # the program -graingrowth: graingrowth.cpp anisotropy.hpp $(core) +graingrowth: graingrowth.cpp $(compiler) $(flags) $< -o $@ -lz -parallel: graingrowth.cpp anisotropy.hpp $(core) +parallel: graingrowth.cpp $(pcompiler) $(pflags) $< -o $@ -lz clean: diff --git a/examples/coarsening/grain_growth/isotropic/Monte_Carlo/Makefile b/examples/coarsening/grain_growth/isotropic/Monte_Carlo/Makefile index 723a2a7..c6ecd86 100644 --- a/examples/coarsening/grain_growth/isotropic/Monte_Carlo/Makefile +++ b/examples/coarsening/grain_growth/isotropic/Monte_Carlo/Makefile @@ -7,21 +7,15 @@ incdir = ../../../../../include # compilers/flags compiler = g++ -flags = -O3 -I $(incdir) pcompiler = mpic++ -pflags = -O3 -I $(incdir) -include mpi.h - -# dependencies -core = $(incdir)/MMSP.main.hpp \ - $(incdir)/MMSP.utility.hpp \ - $(incdir)/MMSP.grid.hpp \ - $(incdir)/MMSP.vector.hpp +flags = -O1 -I $(incdir) +pflags = $(flags) -include mpi.h # the program -graingrowth: graingrowth.cpp $(core) +graingrowth: graingrowth.cpp $(compiler) $(flags) $< -o $@ -lz -parallel: graingrowth.cpp $(core) +parallel: graingrowth.cpp $(pcompiler) $(pflags) $< -o $@ -lz clean: diff --git a/examples/coarsening/grain_growth/isotropic/phase_field/Makefile b/examples/coarsening/grain_growth/isotropic/phase_field/Makefile index bdb3557..97baf9e 100644 --- a/examples/coarsening/grain_growth/isotropic/phase_field/Makefile +++ b/examples/coarsening/grain_growth/isotropic/phase_field/Makefile @@ -7,21 +7,15 @@ incdir = ../../../../../include # compilers/flags compiler = g++ -flags = -O3 -I $(incdir) pcompiler = mpic++ -pflags = -O3 -I $(incdir) -include mpi.h - -# dependencies -core = $(incdir)/MMSP.main.hpp \ - $(incdir)/MMSP.utility.hpp \ - $(incdir)/MMSP.grid.hpp \ - $(incdir)/MMSP.vector.hpp +flags = -O3 -I $(incdir) +pflags = $(flags) -include mpi.h # the program -graingrowth: graingrowth.cpp $(core) +graingrowth: graingrowth.cpp $(compiler) $(flags) $< -o $@ -lz -parallel: graingrowth.cpp $(core) +parallel: graingrowth.cpp $(pcompiler) $(pflags) $< -o $@ -lz clean: diff --git a/examples/coarsening/grain_growth/isotropic/sparsePF/Makefile b/examples/coarsening/grain_growth/isotropic/sparsePF/Makefile index d4c2efe..97baf9e 100644 --- a/examples/coarsening/grain_growth/isotropic/sparsePF/Makefile +++ b/examples/coarsening/grain_growth/isotropic/sparsePF/Makefile @@ -7,21 +7,15 @@ incdir = ../../../../../include # compilers/flags compiler = g++ -flags = -O3 -I $(incdir) pcompiler = mpic++ -pflags = -O3 -I $(incdir) -include mpi.h - -# dependencies -core = $(incdir)/MMSP.main.hpp \ - $(incdir)/MMSP.utility.hpp \ - $(incdir)/MMSP.grid.hpp \ - $(incdir)/MMSP.sparse.hpp +flags = -O3 -I $(incdir) +pflags = $(flags) -include mpi.h # the program -graingrowth: graingrowth.cpp $(core) +graingrowth: graingrowth.cpp $(compiler) $(flags) $< -o $@ -lz -parallel: graingrowth.cpp $(core) +parallel: graingrowth.cpp $(pcompiler) $(pflags) $< -o $@ -lz clean: diff --git a/examples/coarsening/ostwald_ripening/isotropic/phase_field/Makefile b/examples/coarsening/ostwald_ripening/isotropic/phase_field/Makefile index c7cf71f..750204f 100644 --- a/examples/coarsening/ostwald_ripening/isotropic/phase_field/Makefile +++ b/examples/coarsening/ostwald_ripening/isotropic/phase_field/Makefile @@ -7,21 +7,15 @@ incdir = ../../../../../include # compilers/flags compiler = g++ -flags = -O3 -I $(incdir) pcompiler = mpic++ -pflags = -O3 -I $(incdir) -include mpi.h - -# dependencies -core = $(incdir)/MMSP.main.hpp \ - $(incdir)/MMSP.utility.hpp \ - $(incdir)/MMSP.grid.hpp \ - $(incdir)/MMSP.vector.hpp +flags = -O3 -I $(incdir) +pflags = $(flags) -include mpi.h # the program -ostwald: ostwald.cpp $(core) +ostwald: ostwald.cpp $(compiler) $(flags) $< -o $@ -lz -parallel: ostwald.cpp $(core) +parallel: ostwald.cpp $(pcompiler) $(pflags) $< -o $@ -lz clean: diff --git a/examples/coarsening/zener_pinning/anisotropic/Monte_Carlo/Makefile b/examples/coarsening/zener_pinning/anisotropic/Monte_Carlo/Makefile index d2a7ec3..80ba9c8 100644 --- a/examples/coarsening/zener_pinning/anisotropic/Monte_Carlo/Makefile +++ b/examples/coarsening/zener_pinning/anisotropic/Monte_Carlo/Makefile @@ -7,21 +7,15 @@ incdir = ../../../../../include # compilers/flags compiler = g++ -flags = -O3 -I $(incdir) pcompiler = mpic++ -pflags = -O3 -I $(incdir) -include mpi.h - -# dependencies -core = $(incdir)/MMSP.main.hpp \ - $(incdir)/MMSP.utility.hpp \ - $(incdir)/MMSP.grid.hpp \ - $(incdir)/MMSP.scalar.hpp +flags = -O3 -I $(incdir) +pflags = $(flags) -include mpi.h # the program -zener: zener.cpp anisotropy.hpp $(core) +zener: zener.cpp $(compiler) $(flags) $< -o $@ -lz -parallel: zener.cpp anisotropy.hpp $(core) +parallel: zener.cpp $(pcompiler) $(pflags) $< -o $@ -lz clean: diff --git a/examples/coarsening/zener_pinning/anisotropic/phase_field/Makefile b/examples/coarsening/zener_pinning/anisotropic/phase_field/Makefile index 4cbb6d4..c9247f6 100644 --- a/examples/coarsening/zener_pinning/anisotropic/phase_field/Makefile +++ b/examples/coarsening/zener_pinning/anisotropic/phase_field/Makefile @@ -7,21 +7,15 @@ incdir = ../../../../../include # compilers/flags compiler = g++ -flags = -O3 -I $(incdir) pcompiler = mpic++ -pflags = -O3 -I $(incdir) -include mpi.h - -# dependencies -core = $(incdir)/MMSP.main.hpp \ - $(incdir)/MMSP.utility.hpp \ - $(incdir)/MMSP.grid.hpp \ - $(incdir)/MMSP.vector.hpp +flags = -O3 -I $(incdir) +pflags = $(flags) -include mpi.h # the program -zener: zener.cpp anisotropy.hpp $(core) +zener: zener.cpp $(compiler) $(flags) $< -o $@ -lz -parallel: zener.cpp anisotropy.hpp $(core) +parallel: zener.cpp $(pcompiler) $(pflags) $< -o $@ -lz clean: diff --git a/examples/coarsening/zener_pinning/anisotropic/sparsePF/Makefile b/examples/coarsening/zener_pinning/anisotropic/sparsePF/Makefile index 89e0388..c9247f6 100644 --- a/examples/coarsening/zener_pinning/anisotropic/sparsePF/Makefile +++ b/examples/coarsening/zener_pinning/anisotropic/sparsePF/Makefile @@ -7,21 +7,15 @@ incdir = ../../../../../include # compilers/flags compiler = g++ -flags = -O3 -I $(incdir) pcompiler = mpic++ -pflags = -O3 -I $(incdir) -include mpi.h - -# dependencies -core = $(incdir)/MMSP.main.hpp \ - $(incdir)/MMSP.utility.hpp \ - $(incdir)/MMSP.grid.hpp \ - $(incdir)/MMSP.sparse.hpp +flags = -O3 -I $(incdir) +pflags = $(flags) -include mpi.h # the program -zener: zener.cpp anisotropy.hpp $(core) +zener: zener.cpp $(compiler) $(flags) $< -o $@ -lz -parallel: zener.cpp anisotropy.hpp $(core) +parallel: zener.cpp $(pcompiler) $(pflags) $< -o $@ -lz clean: diff --git a/examples/coarsening/zener_pinning/isotropic/Monte_Carlo/Makefile b/examples/coarsening/zener_pinning/isotropic/Monte_Carlo/Makefile index d5825fa..c9247f6 100644 --- a/examples/coarsening/zener_pinning/isotropic/Monte_Carlo/Makefile +++ b/examples/coarsening/zener_pinning/isotropic/Monte_Carlo/Makefile @@ -7,21 +7,15 @@ incdir = ../../../../../include # compilers/flags compiler = g++ -flags = -O3 -I $(incdir) pcompiler = mpic++ -pflags = -O3 -I $(incdir) -include mpi.h - -# dependencies -core = $(incdir)/MMSP.main.hpp \ - $(incdir)/MMSP.utility.hpp \ - $(incdir)/MMSP.grid.hpp \ - $(incdir)/MMSP.scalar.hpp +flags = -O3 -I $(incdir) +pflags = $(flags) -include mpi.h # the program -zener: zener.cpp $(core) +zener: zener.cpp $(compiler) $(flags) $< -o $@ -lz -parallel: zener.cpp $(core) +parallel: zener.cpp $(pcompiler) $(pflags) $< -o $@ -lz clean: diff --git a/examples/coarsening/zener_pinning/isotropic/phase_field/Makefile b/examples/coarsening/zener_pinning/isotropic/phase_field/Makefile index ec1ef0e..c9247f6 100644 --- a/examples/coarsening/zener_pinning/isotropic/phase_field/Makefile +++ b/examples/coarsening/zener_pinning/isotropic/phase_field/Makefile @@ -7,21 +7,15 @@ incdir = ../../../../../include # compilers/flags compiler = g++ -flags = -O3 -I $(incdir) pcompiler = mpic++ -pflags = -O3 -I $(incdir) -include mpi.h - -# dependencies -core = $(incdir)/MMSP.main.hpp \ - $(incdir)/MMSP.utility.hpp \ - $(incdir)/MMSP.grid.hpp \ - $(incdir)/MMSP.vector.hpp +flags = -O3 -I $(incdir) +pflags = $(flags) -include mpi.h # the program -zener: zener.cpp $(core) +zener: zener.cpp $(compiler) $(flags) $< -o $@ -lz -parallel: zener.cpp $(core) +parallel: zener.cpp $(pcompiler) $(pflags) $< -o $@ -lz clean: diff --git a/examples/coarsening/zener_pinning/isotropic/sparsePF/Makefile b/examples/coarsening/zener_pinning/isotropic/sparsePF/Makefile index 62b435c..c9247f6 100644 --- a/examples/coarsening/zener_pinning/isotropic/sparsePF/Makefile +++ b/examples/coarsening/zener_pinning/isotropic/sparsePF/Makefile @@ -7,21 +7,15 @@ incdir = ../../../../../include # compilers/flags compiler = g++ -flags = -O3 -I $(incdir) pcompiler = mpic++ -pflags = -O3 -I $(incdir) -include mpi.h - -# dependencies -core = $(incdir)/MMSP.main.hpp \ - $(incdir)/MMSP.utility.hpp \ - $(incdir)/MMSP.grid.hpp \ - $(incdir)/MMSP.sparse.hpp +flags = -O3 -I $(incdir) +pflags = $(flags) -include mpi.h # the program -zener: zener.cpp $(core) +zener: zener.cpp $(compiler) $(flags) $< -o $@ -lz -parallel: zener.cpp $(core) +parallel: zener.cpp $(pcompiler) $(pflags) $< -o $@ -lz clean: diff --git a/examples/differential_equations/elliptic/Poisson/Makefile b/examples/differential_equations/elliptic/Poisson/Makefile index 85f40ac..de4e740 100644 --- a/examples/differential_equations/elliptic/Poisson/Makefile +++ b/examples/differential_equations/elliptic/Poisson/Makefile @@ -6,20 +6,15 @@ incdir = ../../../../include # compilers/flags compiler = g++ -flags = -O3 -ansi -I $(incdir) pcompiler = mpic++ -pflags = -O3 -ansi -I $(incdir) -include mpi.h - -# dependencies -core = $(incdir)/MMSP.hpp \ - $(incdir)/MMSP.utility.hpp \ - $(incdir)/MMSP.grid.hpp +flags = -O3 -I $(incdir) +pflags = $(flags) -include mpi.h # the program -poisson: poisson.cpp $(core) +poisson: poisson.cpp $(compiler) $(flags) $< -o $@ -lz -parallel: poisson.cpp $(core) +parallel: poisson.cpp $(pcompiler) $(pflags) $< -o $@ -lz clean: diff --git a/examples/phase_transitions/allen-cahn/Makefile b/examples/phase_transitions/allen-cahn/Makefile index afa654b..39fbc09 100644 --- a/examples/phase_transitions/allen-cahn/Makefile +++ b/examples/phase_transitions/allen-cahn/Makefile @@ -7,20 +7,15 @@ incdir = ../../../include # compilers/flags compiler = g++ -flags = -O3 -I $(incdir) pcompiler = mpic++ -pflags = -O3 -I $(incdir) -include mpi.h - -# dependencies -core = $(incdir)/MMSP.main.hpp \ - $(incdir)/MMSP.utility.hpp \ - $(incdir)/MMSP.grid.hpp +flags = -O3 -I $(incdir) +pflags = $(flags) -include mpi.h # the program -allen-cahn: allen-cahn.cpp $(core) +allen-cahn: allen-cahn.cpp $(compiler) $(flags) $< -o $@ -lz -parallel: allen-cahn.cpp $(core) +parallel: allen-cahn.cpp $(pcompiler) $(pflags) $< -o $@ -lz clean: diff --git a/examples/phase_transitions/cahn-hilliard/convex_splitting/Makefile b/examples/phase_transitions/cahn-hilliard/convex_splitting/Makefile index 5467cdb..c746601 100644 --- a/examples/phase_transitions/cahn-hilliard/convex_splitting/Makefile +++ b/examples/phase_transitions/cahn-hilliard/convex_splitting/Makefile @@ -8,25 +8,20 @@ incdir = ../../../../include # compilers/flags compiler = g++ +pcompiler = mpic++ flags = -O3 -DVANILLA -I $(incdir) gflags = -pg -O2 -DVANILLA -Wall -I $(incdir) -pcompiler = mpic++ pflags = $(flags) -include mpi.h -# dependencies -core = $(incdir)/MMSP.main.hpp \ - $(incdir)/MMSP.utility.hpp \ - $(incdir)/MMSP.grid.hpp - # the program -cahn-hilliard: cahn-hilliard.cpp $(core) +cahn-hilliard: cahn-hilliard.cpp $(compiler) $(flags) -fopenmp $< -o $@ -lz -parallel: cahn-hilliard.cpp $(core) +parallel: cahn-hilliard.cpp $(pcompiler) $(pflags) $< -o $@ -lz # for profiling -debug: cahn-hilliard.cpp $(core) +debug: cahn-hilliard.cpp $(compiler) $(gflags) $< -o $@ -lz clean: diff --git a/examples/phase_transitions/cahn-hilliard/explicit/Makefile b/examples/phase_transitions/cahn-hilliard/explicit/Makefile index 70497ec..8238f04 100644 --- a/examples/phase_transitions/cahn-hilliard/explicit/Makefile +++ b/examples/phase_transitions/cahn-hilliard/explicit/Makefile @@ -7,20 +7,15 @@ incdir = ../../../../include # compilers/flags compiler = g++ -flags = -O3 -I $(incdir) pcompiler = mpic++ -pflags = -O3 -I $(incdir) -include mpi.h - -# dependencies -core = $(incdir)/MMSP.main.hpp \ - $(incdir)/MMSP.utility.hpp \ - $(incdir)/MMSP.grid.hpp +flags = -O3 -I $(incdir) +pflags = $(flags) -include mpi.h # the program -cahn-hilliard: cahn-hilliard.cpp $(core) +cahn-hilliard: cahn-hilliard.cpp $(compiler) $(flags) $< -o $@ -lz -parallel: cahn-hilliard.cpp $(core) +parallel: cahn-hilliard.cpp $(pcompiler) $(pflags) $< -o $@ -lz clean: diff --git a/examples/phase_transitions/model_A/Makefile b/examples/phase_transitions/model_A/Makefile index e65c29b..6107a43 100644 --- a/examples/phase_transitions/model_A/Makefile +++ b/examples/phase_transitions/model_A/Makefile @@ -7,20 +7,15 @@ incdir = ../../../include # compilers/flags compiler = g++ -flags = -O3 -I $(incdir) pcompiler = mpic++ -pflags = -O3 -I $(incdir) -include mpi.h - -# dependencies -core = $(incdir)/MMSP.main.hpp \ - $(incdir)/MMSP.utility.hpp \ - $(incdir)/MMSP.grid.hpp +flags = -O3 -I $(incdir) +pflags = $(flags) -include mpi.h # the program -model_A: model_A.cpp $(core) +model_A: model_A.cpp $(compiler) $(flags) $< -o $@ -lz -parallel: model_A.cpp $(core) +parallel: model_A.cpp $(pcompiler) $(pflags) $< -o $@ -lz clean: diff --git a/examples/phase_transitions/model_B/Makefile b/examples/phase_transitions/model_B/Makefile index b3c2695..76da3b1 100644 --- a/examples/phase_transitions/model_B/Makefile +++ b/examples/phase_transitions/model_B/Makefile @@ -7,20 +7,15 @@ incdir = ../../../include # compilers/flags compiler = g++ -flags = -O3 -I $(incdir) pcompiler = mpic++ -pflags = -O3 -I $(incdir) -include mpi.h - -# dependencies -core = $(incdir)/MMSP.main.hpp \ - $(incdir)/MMSP.utility.hpp \ - $(incdir)/MMSP.grid.hpp +flags = -O3 -I $(incdir) +pflags = $(flags) -include mpi.h # the program -model_B: model_B.cpp $(core) +model_B: model_B.cpp $(compiler) $(flags) $< -o $@ -lz -parallel: model_B.cpp $(core) +parallel: model_B.cpp $(pcompiler) $(pflags) $< -o $@ -lz clean: diff --git a/examples/phase_transitions/solidification/anisotropic/Makefile b/examples/phase_transitions/solidification/anisotropic/Makefile index 4b86dc9..b08764d 100644 --- a/examples/phase_transitions/solidification/anisotropic/Makefile +++ b/examples/phase_transitions/solidification/anisotropic/Makefile @@ -8,24 +8,17 @@ incdir = ../../../../include # compilers/flags compiler = g++ -O3 pcompiler = mpic++ -O3 - flags = -I $(incdir) pflags = $(flags) -include mpi.h -# dependencies -core = $(incdir)/MMSP.main.hpp \ - $(incdir)/MMSP.utility.hpp \ - $(incdir)/MMSP.grid.hpp \ - $(incdir)/MMSP.sparse.hpp - # the program -solidification.out: solidification.cpp $(core) +solidification.out: solidification.cpp $(compiler) $(flags) $< -o $@ -lz mmsp2png: mmsp2png.cpp $(core) /usr/include/IL/devil_cpp_wrapper.hpp $(compiler) $(flags) -I /usr/include/IL -include il.h $< -o $@ -lz -lIL -lILU -lILUT -parallel: solidification.cpp $(core) +parallel: solidification.cpp $(pcompiler) $(pflags) $< -o $@ -lz clean: diff --git a/examples/phase_transitions/spinodal/Makefile b/examples/phase_transitions/spinodal/Makefile index b014913..df97f0f 100644 --- a/examples/phase_transitions/spinodal/Makefile +++ b/examples/phase_transitions/spinodal/Makefile @@ -7,20 +7,15 @@ incdir = ../../../include # compilers/flags compiler = g++ -flags = -O3 -I $(incdir) pcompiler = mpic++ -pflags = -O3 -I $(incdir) -include mpi.h - -# dependencies -core = $(incdir)/MMSP.main.hpp \ - $(incdir)/MMSP.utility.hpp \ - $(incdir)/MMSP.grid.hpp +flags = -O3 -I $(incdir) +pflags = $(flags) -include mpi.h # the program -spinodal: spinodal.cpp $(core) +spinodal: spinodal.cpp $(compiler) $(flags) $< -o $@ -lz -parallel: spinodal.cpp $(core) +parallel: spinodal.cpp $(pcompiler) $(pflags) $< -o $@ -lz clean: diff --git a/examples/statistical_mechanics/Heisenberg/Makefile b/examples/statistical_mechanics/Heisenberg/Makefile index 5f3c91b..50d5bc7 100644 --- a/examples/statistical_mechanics/Heisenberg/Makefile +++ b/examples/statistical_mechanics/Heisenberg/Makefile @@ -7,21 +7,15 @@ incdir = ../../../include/ # compilers/flags compiler = g++ -flags = -O3 -I $(incdir) pcompiler = mpic++ -pflags = -O3 -I $(incdir) -include mpi.h - -# dependencies -core = $(incdir)/MMSP.main.hpp \ - $(incdir)/MMSP.utility.hpp \ - $(incdir)/MMSP.grid.hpp \ - $(incdir)/MMSP.vector.hpp +flags = -O3 -I $(incdir) +pflags = $(flags) -include mpi.h # the program -heisenberg: heisenberg.cpp $(core) +heisenberg: heisenberg.cpp $(compiler) $(flags) $< -o $@ -lz -parallel: heisenberg.cpp $(core) +parallel: heisenberg.cpp $(pcompiler) $(pflags) $< -o $@ -lz clean: diff --git a/examples/statistical_mechanics/Ising/Makefile b/examples/statistical_mechanics/Ising/Makefile index 30e3f42..dd7fa6f 100644 --- a/examples/statistical_mechanics/Ising/Makefile +++ b/examples/statistical_mechanics/Ising/Makefile @@ -7,20 +7,15 @@ incdir = ../../../include/ # compilers/flags compiler = g++ -flags = -O3 -I $(incdir) pcompiler = mpic++ -pflags = -O3 -I $(incdir) -include mpi.h - -# dependencies -core = $(incdir)/MMSP.main.hpp \ - $(incdir)/MMSP.utility.hpp \ - $(incdir)/MMSP.grid.hpp +flags = -O3 -I $(incdir) +pflags = $(flags) -include mpi.h # the program -ising: ising.cpp $(core) +ising: ising.cpp $(compiler) $(flags) $< -o $@ -lz -parallel: ising.cpp $(core) +parallel: ising.cpp $(pcompiler) $(pflags) $< -o $@ -lz clean: diff --git a/examples/statistical_mechanics/Potts/Makefile b/examples/statistical_mechanics/Potts/Makefile index f18c735..44e8a18 100644 --- a/examples/statistical_mechanics/Potts/Makefile +++ b/examples/statistical_mechanics/Potts/Makefile @@ -7,20 +7,15 @@ incdir = ../../../include/ # compilers/flags compiler = g++ -flags = -O3 -I $(incdir) pcompiler = mpic++ -pflags = -O3 -I $(incdir) -include mpi.h - -# dependencies -core = $(incdir)/MMSP.main.hpp \ - $(incdir)/MMSP.utility.hpp \ - $(incdir)/MMSP.grid.hpp +flags = -O3 -I $(incdir) +pflags = $(flags) -include mpi.h # the program -potts: potts.cpp $(core) +potts: potts.cpp $(compiler) $(flags) $< -o $@ -lz -parallel: potts.cpp $(core) +parallel: potts.cpp $(pcompiler) $(pflags) $< -o $@ -lz clean: diff --git a/test/build_examples.sh b/test/build_examples.sh index a53c069..f3a86ab 100755 --- a/test/build_examples.sh +++ b/test/build_examples.sh @@ -31,6 +31,7 @@ RunERR=0 nSerBld=0 nParBld=0 nParRun=0 +MFLAG="-s" # Set execution parameters ITERS=1000 @@ -50,7 +51,7 @@ do case $key in --force) echo -n ", forcing build" - MFLAG="-B" + MFLAG="-Bs" ;; --clean) echo -n ", cleaning up after" @@ -130,17 +131,30 @@ do cd $examples/${exdirs[$i]} printf "%2d/%2d %-50s\t" $j $n ${exdirs[$i]} echo $(date) >test.log - make $MFLAG >>test.log && ((nSerBld++)) || (((SerERR++)) && tail test.log) - make $MFLAG parallel >>test.log && ((nParBld++)) || (((ParERR++)) && tail test.log) - if [[ ! $NEXEC ]] + if make $MFLAG + then + ((nSerBld++)) + else + ((SerERR++)) + tail test.log + fi + if make $MFLAG parallel + then + ((nParBld++)) + else + ((ParERR++)) + tail test.log + fi + if [[ -f parallel ]] && [[ ! $NEXEC ]] then # Remove existing images first. If the test fails, # no images in the directory is an obvious red flag. rm -f test.*.png # Run the example in parallel, for speed. - mpirun -np $CORES ./parallel --example 2 test.0000.dat >>test.log && \ - mpirun -np $CORES ./parallel test.0000.dat $ITERS $INTER >>test.log && \ - ((nParRun++)) || (((RunERR++)) && tail test.log) + mpirun -np $CORES ./parallel --example 2 test.0000.dat >>test.log \ + && mpirun -np $CORES ./parallel test.0000.dat $ITERS $INTER >>test.log \ + && ((nParRun++)) \ + || ((RunERR++)) if [[ ! $NOVIZ ]] then # Show the result From 8006afad8c38754cd8550937567f9c71404580b2 Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Fri, 19 Feb 2016 15:51:38 -0500 Subject: [PATCH 59/83] Clear memory error due to uninitialized array values --- .../anisotropic/Monte_Carlo/Makefile | 2 +- .../anisotropic/Monte_Carlo/graingrowth.cpp | 39 +++++++++---------- .../isotropic/Monte_Carlo/Makefile | 2 +- .../isotropic/Monte_Carlo/graingrowth.cpp | 14 +++---- 4 files changed, 28 insertions(+), 29 deletions(-) diff --git a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/Makefile b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/Makefile index 4ad5f81..e75203d 100644 --- a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/Makefile +++ b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/Makefile @@ -8,7 +8,7 @@ incdir = ../../../../../include # compilers/flags compiler = g++ pcompiler = mpic++ -flags = -O1 -I $(incdir) +flags = -O3 -I $(incdir) gflags = -g -I $(incdir) pflags = $(flags) -include mpi.h diff --git a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp index 281ab3b..55e65c3 100644 --- a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp +++ b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp @@ -10,7 +10,7 @@ #ifndef GRAINGROWTH_UPDATE #define GRAINGROWTH_UPDATE #include -#include +#include #include"MMSP.hpp" #include"graingrowth.hpp" @@ -69,30 +69,31 @@ template void update(grid& mcGrid, int steps) /*---------------generate cells------------------*/ int dimension_length = 0, num_lattice_cells = 1; - int lattice_cells_each_dimension[dim]; + vector lattice_cells_each_dimension(dim,0); for (int i = 0; i < dim; i++) { dimension_length = x1(mcGrid, i) - x0(mcGrid, i); - if (x0(mcGrid, 0) % 2 == 0) + if (x0(mcGrid, 0) % 2 == 0) // in serial, this is always true lattice_cells_each_dimension[i] = dimension_length / 2 + 1; else lattice_cells_each_dimension[i] = 1 + (dimension_length % 2 == 0 ? dimension_length / 2 : dimension_length / 2 + 1); num_lattice_cells *= lattice_cells_each_dimension[i]; } - vector x (dim, 0); - vector x_prim (dim, 0); - int initial_coordinates[dim]; + vector x(dim, 0); + vector x_prim(dim, 0); + vector initial_coordinates(dim,0); - int num_grids_to_flip[( static_cast(pow(2, dim)) )]; - int first_cell_start_coordinates[dim]; - for (int kk = 0; kk < dim; kk++) first_cell_start_coordinates[kk] = x0(mcGrid, kk); - for (int i = 0; i < dim; i++) { - if (x0(mcGrid, i) % 2 != 0) first_cell_start_coordinates[i]--; - } + vector num_grids_to_flip(int(pow(2, dim)),0); + vector first_cell_start_coordinates(dim,0); + for (int kk = 0; kk < dim; kk++) + first_cell_start_coordinates[kk] = x0(mcGrid, kk); + for (int i = 0; i < dim; i++) + if (x0(mcGrid, i) % 2 != 0) + first_cell_start_coordinates[i]--; for (int j = 0; j < num_lattice_cells; j++) { - int cell_coords_selected[dim]; + vector cell_coords_selected(dim,0); if (dim == 2) { cell_coords_selected[dim - 1] = j % lattice_cells_each_dimension[dim - 1]; //1-indexed cell_coords_selected[0] = (j / lattice_cells_each_dimension[dim - 1]); @@ -101,9 +102,8 @@ template void update(grid& mcGrid, int steps) cell_coords_selected[1] = (j / lattice_cells_each_dimension[dim - 1]) % lattice_cells_each_dimension[1]; cell_coords_selected[0] = ( j / lattice_cells_each_dimension[dim - 1] ) / lattice_cells_each_dimension[1]; } - for (int i = 0; i < dim; i++) { + for (int i = 0; i < dim; i++) x[i] = first_cell_start_coordinates[i] + 2 * cell_coords_selected[i]; - } if (dim == 2) { x_prim = x; @@ -163,10 +163,9 @@ template void update(grid& mcGrid, int steps) for (int k = 0; k < dim; k++) initial_coordinates[k] = x0(mcGrid, k); - for (int i = 0; i < dim; i++) { + for (int i = 0; i < dim; i++) if (x0(mcGrid, i) % 2 != 0) initial_coordinates[i]--; - } for (int step = 0; step < steps; step++) { if (rank == 0) @@ -176,7 +175,7 @@ template void update(grid& mcGrid, int steps) else if (dim == 3) num_sublattices = 8; for (int sublattice = 0; sublattice < num_sublattices; sublattice++) { - vector x (dim, 0); + vector x(dim, 0); // This particular algorithm requires that srand() be called here. unsigned long seed=time(NULL); #ifdef MPI_VERSION @@ -186,7 +185,7 @@ template void update(grid& mcGrid, int steps) for (int hh = 0; hh < num_grids_to_flip[sublattice]; hh++) { int cell_numbering = rand() % (num_lattice_cells); //choose a cell to flip, from 0 to num_cells_in_thread-1 - int cell_coords_selected[dim]; + vector cell_coords_selected(dim,0); if (dim == 2) { cell_coords_selected[dim - 1] = cell_numbering % lattice_cells_each_dimension[dim - 1]; //1-indexed cell_coords_selected[0] = (cell_numbering / lattice_cells_each_dimension[dim - 1]); @@ -369,7 +368,7 @@ template void update(grid& mcGrid, int steps) #ifdef MPI_VERSION MPI::COMM_WORLD.Barrier(); #endif - ghostswap(mcGrid, sublattice); // once looped over a "color", ghostswap. + //ghostswap(mcGrid, sublattice); // once looped over a "color", ghostswap. }//loop over sublattice }//loop over step }//update diff --git a/examples/coarsening/grain_growth/isotropic/Monte_Carlo/Makefile b/examples/coarsening/grain_growth/isotropic/Monte_Carlo/Makefile index c6ecd86..7483eed 100644 --- a/examples/coarsening/grain_growth/isotropic/Monte_Carlo/Makefile +++ b/examples/coarsening/grain_growth/isotropic/Monte_Carlo/Makefile @@ -8,7 +8,7 @@ incdir = ../../../../../include # compilers/flags compiler = g++ pcompiler = mpic++ -flags = -O1 -I $(incdir) +flags = -O3 -I $(incdir) pflags = $(flags) -include mpi.h # the program diff --git a/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp b/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp index d30a7c3..6e94073 100644 --- a/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp +++ b/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp @@ -9,7 +9,7 @@ #ifndef GRAINGROWTH_UPDATE #define GRAINGROWTH_UPDATE #include -#include +#include #include"MMSP.hpp" #include"graingrowth.hpp" @@ -71,7 +71,7 @@ template void update(grid& mcGrid, int steps) /*---------------generate cells------------------*/ int dimension_length = 0, num_lattice_cells = 1; - int lattice_cells_each_dimension[dim]; + vector lattice_cells_each_dimension(dim,0); for (int i = 0; i < dim; i++) { dimension_length = x1(mcGrid, i) - x0(mcGrid, i); if (x0(mcGrid, 0) % 2 == 0) @@ -83,10 +83,10 @@ template void update(grid& mcGrid, int steps) vector x (dim, 0); vector x_prim (dim, 0); - int initial_coordinates[dim]; + vector initial_coordinates(dim,0); - int num_grids_to_flip[( static_cast(pow(2, dim)) )]; - int first_cell_start_coordinates[dim]; + vector num_grids_to_flip(int(pow(2, dim)),0); + vector first_cell_start_coordinates(dim,0); for (int kk = 0; kk < dim; kk++) first_cell_start_coordinates[kk] = x0(mcGrid, kk); for (int i = 0; i < dim; i++) { if (x0(mcGrid, i) % 2 != 0) first_cell_start_coordinates[i]--; @@ -94,7 +94,7 @@ template void update(grid& mcGrid, int steps) for (int j = 0; j < num_lattice_cells; j++) { - int cell_coords_selected[dim]; + vector cell_coords_selected(dim,0); if (dim == 2) { cell_coords_selected[dim - 1] = j % lattice_cells_each_dimension[dim - 1]; //1-indexed cell_coords_selected[0] = (j / lattice_cells_each_dimension[dim - 1]); @@ -188,7 +188,7 @@ template void update(grid& mcGrid, int steps) for (int hh = 0; hh < num_grids_to_flip[sublattice]; hh++) { int cell_numbering = rand() % (num_lattice_cells); //choose a cell to flip, from 0 to num_cells_in_thread-1 - int cell_coords_selected[dim]; + vector cell_coords_selected(dim,0); if (dim == 2) { cell_coords_selected[dim - 1] = cell_numbering % lattice_cells_each_dimension[dim - 1]; //1-indexed cell_coords_selected[0] = (cell_numbering / lattice_cells_each_dimension[dim - 1]); From 2966f67a3a003349880d231d0dc25462965bf8dd Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Fri, 19 Feb 2016 17:45:08 -0500 Subject: [PATCH 60/83] Output redirect and return status capture --- .travis.yml | 2 +- algorithms/generators.cpp | 14 +++--- .../convex_splitting/cahn-hilliard.cpp | 6 +-- .../anisotropic/solidification.cpp | 2 +- include/MMSP.grid.hpp | 23 +++++---- include/MMSP.main.hpp | 24 +++++----- include/MMSP.utility.hpp | 14 ++++-- test/build_examples.sh | 47 +++++++++++++------ 8 files changed, 77 insertions(+), 55 deletions(-) diff --git a/.travis.yml b/.travis.yml index e42e7f9..08db0e3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,4 +21,4 @@ before_script: - nproc script: - ./build_examples.sh --noexec --noviz && ./build_examples.sh --noviz --clean + ./build_examples.sh -n 2 --noviz diff --git a/algorithms/generators.cpp b/algorithms/generators.cpp index e37db0f..437646d 100644 --- a/algorithms/generators.cpp +++ b/algorithms/generators.cpp @@ -44,7 +44,7 @@ int seeds_to_buffer(const std::vector >& vp, int* &q) { // q should already point to an array of T[size] if (q == NULL) { std::cerr << "\nError in seeds_to_buffer: send_buffer not initialized." << std::endl; - exit(1); + MMSP::Abort(-1); } int* p = q; for (unsigned int i=0; i >& vp, int* &q) { void seeds_from_buffer(std::vector >& vp, int* &q, const int& size) { if (q == NULL) { std::cerr << "\nError in seeds_from_buffer: recv_buffer not initialized." << std::endl; - exit(1); + MMSP::Abort(-1); } MMSP::vector v(3); for (int* p = q + 2; p < q + size; p += 3) { @@ -396,7 +396,7 @@ void approximate_voronoi(MMSP::grid >& grid, const std::vec else { std::cerr << "Error: Invalid dimension (" << dim << ") in tessellation." << std::endl; - std::exit(1); + MMSP::Abort(-1); } #ifdef DEBUG #ifdef MPI_VERSION @@ -512,13 +512,13 @@ void seeds_from_file(const int x0[dim], const int x1[dim], const int g0[dim], co std::ifstream input(seedfilename); if (!input) { std::cerr<<"\nError: "<> seed_dim; if (dim!=seed_dim) { std::cerr<<"\nError: "< > local_seeds; // blank for now seeds.clear(); @@ -531,7 +531,7 @@ void seeds_from_file(const int x0[dim], const int x1[dim], const int g0[dim], co input >> s; if (s>1.0001) { std::cerr<<"\nError: Seeds in "< >& oldGrid, int steps) if (iter==max_iter) { if (rank==0) std::cerr<<" Solver stagnated on step "< #include #include -#include #include #include #include @@ -1340,7 +1339,7 @@ class grid if (!input) { std::cerr << "File input error: could not open "; std::cerr << filename << "." << std::endl; - exit(-1); + MMSP::Abort(-1); } // grid data type error check @@ -1348,7 +1347,7 @@ class grid getline(input, type, '\n'); if (type != name(*this)) { std::cerr << "File read error: wrong data type (" << type << ")." << std::endl; - exit(-2); + MMSP::Abort(-2); } // dimension error check @@ -1356,7 +1355,7 @@ class grid input >> dimen; if (dimen != dim) { std::cerr << "File read error: wrong dimension (" << dimen << ")." << std::endl; - exit(-3); + MMSP::Abort(-3); } // read number of fields @@ -1455,7 +1454,7 @@ class grid if (size_in_mem!=size_on_disk) { #ifdef RAW std::cerr<<"Unable to uncompress data: compiled without zlib."< #include #include -#include #include #include @@ -28,6 +27,14 @@ void Finalize() { #endif } +// MMSP Abort function +void Abort(int err) { +#ifdef MPI_VERSION + MPI::COMM_WORLD.Abort(err); +#endif + exit(err); +} + // MMSP boundary conditions enum { @@ -353,9 +360,8 @@ void print_progress(const int step, const int steps) { std::cout<<"No. "<<1+iterations/steps<<":\t"<>test.log \ - && mpirun -np $CORES ./parallel test.0000.dat $ITERS $INTER >>test.log \ - && ((nParRun++)) \ - || ((RunERR++)) + && mpirun -np $CORES ./parallel test.0000.dat $ITERS $INTER >>test.log + # Return codes are tricky! Try testing bash $? variable + if [[ $? ]] + then + ((nParRun++)) + else + ((RunERR++)) + tail test.log + fi if [[ ! $NOVIZ ]] then # Show the result @@ -178,7 +198,7 @@ do exfin=$(date +%s) exlapse=$(echo "$exfin-$exstart" | bc -l) - echo "${exlapse} seconds" + printf "%3d seconds\n" $exlapse done cd ${examples} @@ -186,8 +206,7 @@ cd ${examples} tfinish=$(date +%s) elapsed=$(echo "$tfinish-$tstart" | bc -l) -echo -echo "${elapsed} seconds elapsed." +printf "%67d seconds elapsed.\n" $elapsed echo "${nSerBld} serial examples built successfully, ${SerERR} failed." echo "${nParBld} parallel examples built successfully, ${ParERR} failed." echo "${nParRun} parallel examples executed successfully, ${RunERR} failed." From 035a2b7b68a3f18ba569b99a5e485117ab1abd90 Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Fri, 19 Feb 2016 17:47:38 -0500 Subject: [PATCH 61/83] duplicate namespace --- .../cahn-hilliard/convex_splitting/cahn-hilliard.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/phase_transitions/cahn-hilliard/convex_splitting/cahn-hilliard.cpp b/examples/phase_transitions/cahn-hilliard/convex_splitting/cahn-hilliard.cpp index 9bb8d93..07adadc 100644 --- a/examples/phase_transitions/cahn-hilliard/convex_splitting/cahn-hilliard.cpp +++ b/examples/phase_transitions/cahn-hilliard/convex_splitting/cahn-hilliard.cpp @@ -183,7 +183,7 @@ void generate(int dim, const char* filename) if (dim!=2) { std::cerr<<"ERROR: Convex splitting discretization is only 2-D, for now."< >& oldGrid, int steps) if (iter==max_iter) { if (rank==0) std::cerr<<" Solver stagnated on step "< Date: Sat, 20 Feb 2016 15:07:48 -0500 Subject: [PATCH 63/83] improved automatic error detection --- .travis.yml | 3 +- test/build_examples.sh | 125 ++++++++++++++++++++++------------------- 2 files changed, 68 insertions(+), 60 deletions(-) diff --git a/.travis.yml b/.travis.yml index 08db0e3..3087f62 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,7 +18,6 @@ install: before_script: - cd test - - nproc script: - ./build_examples.sh -n 2 --noviz + ./build_examples.sh --np 2 --noviz --clean diff --git a/test/build_examples.sh b/test/build_examples.sh index 196cec2..922bc1b 100755 --- a/test/build_examples.sh +++ b/test/build_examples.sh @@ -15,21 +15,21 @@ # beginners_diffusion -- does not match execution pattern # Valid flags are: -# --noexec: build in serial and parallel, but do not execute -# --noviz: do not convert data for visualization -# --force: pass -B flag to make -# -n X: pass mpirun X ranks (e.g., -n 3 yields mpirun -np 3) -# --short: execute tests .1x default -# --long: execute tests 5x longer than default -# --extra: execute tests 25x longer than default -# --clean: delete binary files after test completes -# --purge: delete binaries, data, and images after test completes +# --noexec build in serial and parallel, but do not execute +# --noviz do not convert data for visualization +# --force pass -B flag to make +# --np X pass mpirun X ranks (e.g., --np 3 yields mpirun -np 3) +# --short execute tests .1x default +# --long execute tests 5x longer than default +# --extra execute tests 25x longer than default +# --clean delete binary files after test completes +# --purge delete binaries, data, and images after test completes # Initialize timer and completion counters tstart=$(date +%s) -SerERR=0 -ParERR=0 -RunERR=0 +nSerErr=0 +nParErr=0 +nRunErr=0 nSerBld=0 nParBld=0 nParRun=0 @@ -40,6 +40,10 @@ ITERS=1000 INTER=500 CORES=4 COREMAX=$(nproc) +if [[ $CORES > $COREMAX ]] +then + CORES=$COREMAX +fi # Get going cd ../examples @@ -53,55 +57,56 @@ do key="$1" case $key in --force) - echo -n ", forcing build" - MFLAG="-Bs" + echo -n ", forcing build" + MFLAG="-Bs" ;; - --clean) - echo -n ", cleaning up after" + --clean) + echo -n ", cleaning up after" CLEAN=true ;; --purge) - echo -n ", cleaning up after" - CLEAN=true - PURGE=true + echo -n ", cleaning up after" + CLEAN=true + PURGE=true ;; --noexec) - echo -n ", not executing" - NEXEC=true + echo -n ", not executing" + NEXEC=true ;; --short) - echo -n ", taking 100 steps" - ITERS=100 - INTER=100 + ITERS=$(($ITERS/10)) + INTER=100 ;; --long) - echo -n ", taking 5,000 steps" - ITERS=5000 - INTER=1000 + ITERS=$((5*$ITERS)) + INTER=1000 ;; --extra) - echo -n ", taking 25,000 steps" - ITERS=25000 - INTER=5000 + ITERS=$((25*$ITERS)) + INTER=5000 ;; --noviz) - echo -n ", not converting to PNG" - NOVIZ=true + echo -n ", no PNG output" + NOVIZ=true ;; - -n) - shift - CORES=$1 - echo -n ", $CORES/$COREMAX MPI ranks" + --np) + shift + CORES=$1 ;; *) - echo "WARNING: Unknown option ${key}." - echo - ;; + echo "WARNING: Unknown option ${key}." + echo + ;; esac shift # pop first entry from command-line argument list, reduce $# by 1 done -echo +if [[ ! $NEXEC ]] +then + echo ", taking $ITERS steps, using $CORES/$COREMAX MPI ranks" +else + echo +fi if [[ ! $NOVIZ ]] then @@ -146,34 +151,37 @@ do exstart=$(date +%s) j=$(($i+1)) cd $examples/${exdirs[$i]} - printf "%2d/%2d %-50s\t" $j $n ${exdirs[$i]} + printf "%2d/%2d %-52s\t" $j $n ${exdirs[$i]} echo $(date) >test.log if make $MFLAG then ((nSerBld++)) else - ((SerERR++)) - tail test.log + ((nSerErr++)) fi if make $MFLAG parallel then ((nParBld++)) else - ((ParERR++)) - tail test.log + ((nParErr++)) fi if [[ -f parallel ]] && [[ ! $NEXEC ]] then # Run the example in parallel, for speed. - mpirun -np $CORES ./parallel --example 2 test.0000.dat >>test.log \ - && mpirun -np $CORES ./parallel test.0000.dat $ITERS $INTER >>test.log - # Return codes are tricky! Try testing bash $? variable - if [[ $? ]] + mpirun -np $CORES ./parallel --example 2 test.0000.dat 1>test.log 2>error.log \ + && mpirun -np $CORES ./parallel test.0000.dat $ITERS $INTER 1>>test.log 2>>error.log + # Return codes are not reliable. Litter the hard drive with files instead. + if [[ -f error.log ]] && [[ $(wc -w error.log) > 1 ]] then - ((nParRun++)) + ((nRunErr++)) + wc -w error.log + if [[ -f error.log ]] + then + head error.log + fi else - ((RunERR++)) - tail test.log + ((nParRun++)) + rm -f error.log fi if [[ ! $NOVIZ ]] then @@ -189,10 +197,11 @@ do then make -s clean rm -f test.*.dat + rm -f test.log + rm -f error.log if [[ $PURGE ]] then rm -f test.*.png - rm -f test.log fi fi @@ -206,14 +215,14 @@ cd ${examples} tfinish=$(date +%s) elapsed=$(echo "$tfinish-$tstart" | bc -l) -printf "%67d seconds elapsed.\n" $elapsed -echo "${nSerBld} serial examples built successfully, ${SerERR} failed." -echo "${nParBld} parallel examples built successfully, ${ParERR} failed." -echo "${nParRun} parallel examples executed successfully, ${RunERR} failed." +printf "Elapsed time: %53d seconds\n" $elapsed echo +printf "%2d serial examples compiled successfully, %2d failed.\n" $nSerBld $nSerErr +printf "%2d parallel examples compiled successfully, %2d failed.\n" $nParBld $nParErr +printf "%2d parallel examples executed successfully, %2d failed.\n" $nParRun $nRunErr cd ../test/ -AllERR=$(echo "$SerERR+$ParERR+$RunERR" | bc -l) +AllERR=$(echo "$nSerErr+$nParErr+$nRunErr" | bc -l) if [[ $AllERR > 0 ]] then echo "${AllERR} tests failed." From ac976517ffe0028c0dd2c6ad031d1cc7c189aa68 Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Sat, 20 Feb 2016 16:40:24 -0500 Subject: [PATCH 64/83] update documentation with automated build instructions --- doc/MMSP.chapter2.tex | 109 +++++++++++++++++++++++++++++++----------- doc/MMSP.manual.pdf | Bin 328736 -> 332102 bytes 2 files changed, 82 insertions(+), 27 deletions(-) diff --git a/doc/MMSP.chapter2.tex b/doc/MMSP.chapter2.tex index f7d007b..7ad2f12 100644 --- a/doc/MMSP.chapter2.tex +++ b/doc/MMSP.chapter2.tex @@ -7,10 +7,10 @@ \section{Download} The source code for \MMSP\ is available through a {\tt git} repository, hosted on \href{https://github.com/mesoscale/mmsp}{GitHub}. This requires the \href{http://www.git-scm.com}{\tt git} version control software to be installed on the target machine, as well as a working internet connection. From the command line, type \begin{shadebox} \begin{verbatim} - git clone https://github.org/mesoscale/mmsp.git MMSP + git clone https://github.org/mesoscale/mmsp.git \end{verbatim} \end{shadebox} -If the checkout is successful, then there is no need to perform the steps for intstallation described below, and the user should move on to setup. +If the checkout is successful, the latest version of \MMSP\ will be copied into a sub-directory named {\tt mmsp}. There is no need to perform the steps for installation described below, and the user should move on to setup. Having a local copy of \MMSP\ makes it simple to keep your code up-to-date. From the root folder, simply type \begin{shadebox} @@ -21,28 +21,22 @@ \section{Download} to update a working copy with the latest version of the \MMSP\ source code. See the {\tt git} documentation for more details on version control. \section{Installation} -After an appropriate source code archive is obtained as described above, the next step is to install \MMSP. This should be as simple as unpacking the archive. Users with administrator priveledges may choose to install the \MMSP\ header files in a location that will be searched by their compiler's preprocessor, but we do not describe how to do this here. The following paragraphs provide platform-specific instructions for a local installation. +If {\tt git} is unavailable or not desired, the user should download the \MMSP\ source code archive by clicking the link to ``Download ZIP'' from \href{https://github.com/mesoscale/mmsp}{https://github.com/mesoscale/mmsp}. Installation should be as simple as unpacking the archive. Users with administrator privileges may choose to install the \MMSP\ header files in a location that will be searched by their compiler's preprocessor, but we do not describe how to do this here. The following paragraphs provide platform-specific instructions for a local installation. \paragraph{Linux/Unix} Local installation for Linux users should simply involve unpacking the archive. As most Linux systems have means to unpack both tarballs and zip files, there is likely no reason to prefer either. After downloading the archive file, move it to the directory where you want \MMSP\ to reside, making sure that you have read access to the file as well as write access to the directory. Then issue a command to unpack the archive, e.g. \begin{shadebox} \begin{verbatim} - tar zxf MMSP.3.0.6.tar.gz + unzip mmsp-master.zip && mv mmsp-master mmsp \end{verbatim} \end{shadebox} -or +This will unpack the contents of the archive into a folder named {\tt mmsp}. Next, type \begin{shadebox} \begin{verbatim} - unzip MMSP.3.0.6.zip + ls mmsp \end{verbatim} \end{shadebox} -This will unpack the contents of the archive into a folder named \MMSP. Next, type -\begin{shadebox} -\begin{verbatim} - ls MMSP -\end{verbatim} -\end{shadebox} -which should indicate that folders such as {\tt MMSP/doc}, {\tt MMSP/examples}, etc.~have been created. If either command fails or the folder \MMSP\ is not created, check the {\tt tar} or {\tt unzip} documentation. +which should indicate that folders such as {\tt mmsp/doc}, {\tt mmsp/examples}, etc.~have been created. If either command fails or the folder {\tt mmsp} is not created, check the {\tt tar} or {\tt unzip} documentation. \paragraph{Mac OS} Mac OS users will follow much of the same procedure as Linux users, so it is advisible to read the previous section on Linux installation. For those uninitiated, or who have never had any previous reason to use it, the {\tt Terminal} application can be found under {\tt Applications/Utilities}. Again, all steps described above for Linux installation should apply here as well. @@ -53,7 +47,33 @@ \section{Installation} It is also possible to compile \MMSP\ source code within a code development environment such as Visual Studio, however, \MMSP\ code is typically so simple that any code management beyond command line or makefile compilation is only a hinderance. \section{Setup} -Once \MMSP\ has been installed, there are a few useful tests and utility programs that should be generated. First, enter the {\tt MMSP/test} directory and type +Once \MMSP\ has been installed, there are a few useful tests and utility programs that should be generated. First, enter the directory {\tt MMSP/utility} and type +\begin{shadebox} +\begin{verbatim} + make +\end{verbatim} +\end{shadebox} +This will produce a number of conversion programs. These include +\begin{itemize} + \item {\tt mmsp2vti}, which converts \MMSP\ grid data files into formats that can be read by visualization software such as \href{http://www.paraview.org}{\tt ParaView}, + \item {\tt mmsp2png}, which converts \MMSP\ grid data files Portable Network Graphics (PNG), and + \item several utilities for translating between different types of \MMSP\ grid data. +\end{itemize} +Because the programs provided in this directory are used so often, \MMSP\ users may wish to add the {\tt MMSP/utility} directory to their command path. This can be achieved by adding the following lines to their {\tt \$HOME/.bashrc} file, +\begin{shadebox} +\begin{verbatim} + export PATH=$PATH:$HOME/mmsp/utility +\end{verbatim} +\end{shadebox} +for users of the {\tt bash} shell, or +\begin{shadebox} +\begin{verbatim} + setenv PATH $PATH:$HOME/mmsp/utility +\end{verbatim} +\end{shadebox} +for users of the {\tt tcsh} shell. If you cloned or extracted \MMSP\ into a path other than {\tt ~/mmsp}, e.g. {\tt ~/Downloads/mmsp}, specify the appropriate path in the environmental variable. + +Next, enter the {\tt MMSP/test} directory and type \begin{shadebox} \begin{verbatim} make test @@ -75,40 +95,75 @@ \section{Setup} If the test program fails to compile, it is most likely because either {\tt make} or {\tt g++} (the GNU {\tt c++} compiler) is not installed on the system or is not configured properly. Of course, any other ISO-compliant {\tt c++} compiler may be used instead. If there is a problem at this stage, users should check their configuration. -Next, enter the directory {\tt MMSP/utility} and type +Those who plan to use \MMSP\ with the MPI (Message Passing Interface) libraries should also take this opportunity to test their MPI configuration. To do this, type \begin{shadebox} \begin{verbatim} - make utility + make parallel \end{verbatim} \end{shadebox} -This will produce a number of conversion programs. In particular, it will produce several programs such as {\tt mmsp2vti} which can be used to convert \MMSP\ grid data files into formats that can be read by visualization software such as \href{http://www.paraview.org}{\tt ParaView}. Because the programs provided in this directory are used so often, \MMSP\ users may wish to add the {\tt MMSP/utility} directory to their command path. This can be achieved by adding the following lines to their {\tt \$HOME/.bashrc} file, +which, if successful, produces a parallel version of the test program. +If {\tt make} is not installed, type \begin{shadebox} \begin{verbatim} - PATH=$PATH:MMSP/utility - export PATH + mpic++ -I../include -include mpi.h test.cpp -o parallel \end{verbatim} \end{shadebox} -for users of the {\tt bash} shell, or +Once the code is compiled, run the program using an appropriate command for your MPI distribution, e.g. \begin{shadebox} \begin{verbatim} - setenv PATH $PATH:MMSP/utility + mpirun -np 4 parallel \end{verbatim} \end{shadebox} -for users of the {\tt tcsh} shell. +Note that for successful compilation, the MPI distribution is expected to provide a compilation script named {\tt mpic++} and a header file named {\tt mpi.h}. If the program fails to compile, it may be that the user's MPI distribution provides {\tt mpicxx}, {\tt mpiCC}, or the like instead of {\tt mpic++}, or that it provides {\tt mpicxx.h} or something similar instead of {\tt mpi.h}. In this case, the user should edit the {\tt Makefile} accordingly. Likewise, the appropriate command to run the compiled program may differ depending on the MPI distribution. This may take the form of, e.g.\ {\tt mpiexec}, instead of {\tt mpirun}. If the user has multiple versions of MPI installed (e.g., OpenMPI and MPICH2) side-by-side, runtime errors will occur if the program compiles against one library and executes against the other. Managing such an environment is beyond the scope of this document, but the \MMSP\ developers may be able to assist over e-mail. -Finally, those who plan to use \MMSP\ with the MPI (Message Passing Interface) libraries should also take this opportunity to test their MPI configuration. To do this, return to the directory {\tt MMSP/test} and type +Finally, the full \MMSP\ codebase can be tested by compiling and running the example programs. While this can be done one-by-one, it is more efficient to execute the {\tt build\_examples.sh} script by typing \begin{shadebox} \begin{verbatim} - make parallel + ./build_examples.sh \end{verbatim} \end{shadebox} -which, if successful, produces a parallel version of the test program. Once the code is compiled, run the program using an appropriate command for your MPI distribution, e.g.\ +The script takes the following actions for each example: +\begin{enumerate} + \item Compile for serial using {\tt g++} + \item Compile for parallel using {\tt mpic++} + \item Execute in parallel, taking 1000 timesteps + \item Convert each of the stored checkpoint files to PNG for visual inspection + \item Summarize compiler and runtime errors, if encountered. +\end{enumerate} +The script accepts command-line arguments, summarized in the first few lines of the script. Open it in a text editor to see the options and recommended usage. This same script is used on the \href{http://travis-ci.org}{Travis Continuous Integration} service to test the \MMSP\ source code and proposed changes for compiler and runtime problems. If the script encounters errors on your machine, you can check whether the problem is local or systematic by visiting the \MMSP\ project page at \href{https://travis-ci.org/mesoscale/mmsp}{Travis-CI} or \href{https://github.com/mesoscale/mmsp}{GitHub}. + +\section{Advanced Setup} +Users are encouraged to copy the \MMSP\ example implementations as starting points for new projects. To protect the work from being over-written when \MMSP\ is updated, the user is advised to copy the example code into a directory outside the \MMSP\ installation path, then set appropriate environmental variables in the {\tt bash} profile and the {\tt Makefile}. To do this, append {\tt \$HOME/.bashrc} with \begin{shadebox} \begin{verbatim} - mpirun -np 4 parallel + export MMSP_PATH=$HOME/mmsp +\end{verbatim} +\end{shadebox} +and, in the project {\tt Makefile}, replace statements similar to +\begin{shadebox} +\begin{verbatim} + incdir=../../../../include +\end{verbatim} +\end{shadebox} +with +\begin{shadebox} +\begin{verbatim} + incdir=$(MMSP_PATH) +\end{verbatim} +\end{shadebox} +Finally, if customization of {\tt main()} is needed, copy the generic {\tt MMSP.main.hp} using +\begin{shadebox} +\begin{verbatim} + cp $MMSP_PATH/include/MMSP.main.hpp main.cpp +\end{verbatim} +\end{shadebox} +then specify the new filename, {\tt main.cpp}, in the {\tt \#include} statement at the foot of the {\tt .cpp} file. Build your custom project with one of +\begin{shadebox} +\begin{verbatim} + make + make parallel \end{verbatim} \end{shadebox} -Note that for successful compilation, the MPI distribution is expected to provide a compilation script named {\tt mpic++} and a the header file named {\tt mpi.h}. If the program fails to compile, it may be that the user's MPI distribution provides {\tt mpicxx}, {\tt mpiCC}, or the like instead of {\tt mpic++}, or that it provides {\tt mpicxx.h} or something similar instead of {\tt mpi.h}. In this case, the user should edit the {\tt Makefile} accordingly. Likewise, the appropriate command to run the compiled program may differ depending on the MPI distribution. This may take the form of, e.g.\ {\tt mpiexec}, instead of {\tt mpirun}. Unfortunately, MPI distributions do not adhere to a single standard with respect to compiling and running parallel programs, and so it is largely left to the user to determine what must be done for their particular system. \section{Support} \MMSP\ is not commercial code and there are no guarantees or claims, stated or implied, pertaining to its fitness for any purpose. \MMSP\ is intended soley for use in non-profit scientific research. In spite of this, the \MMSP\ team is devoted to producing a quality product that addresses the needs of the scientific community. Please do not hesitate to contact our development team with any questions or suggestions. Contact information can be found on the \MMSP\ page at \href{https://github.com/mesoscale}{GitHub}. diff --git a/doc/MMSP.manual.pdf b/doc/MMSP.manual.pdf index c7baea652c71d341d501f327ba2e1fc4236bb048..2a57802dba7036857ba07e570034db3029bf3245 100644 GIT binary patch delta 253840 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�#&7#$cR++z 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

DO_H2ij(ce)F4Gb|EcRz0O$n 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�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 z znTcn^sR1_2HC6B=5Nf0ASg7J!`%UK`RrM;q!vm>5DQO;S^tB&~br5ru2wiSHh84M2 z#+P5f?w}g&&Cfvxs~)zEEfu*ZNAT8`d{EDT_xH(Z)72BP9sX<@!WhjmNWA>j*GF(- zXxwlh3?5g~fL~wI{$bKeC2eUTg(UUg`S;+Gel0y@DkFhB;dtKJIV-wmmQ!=FjZ=29 zfbO>?*59^ga(ggAtKEfH9piYHa}j5V*ajhzBmJsUwRp1ICz|S*80ZvPpM6iXG@+M( zJJbaicCy91`j<9}*UA9y_vudui55|XlJvn=S$_cJ)XmGZ&{pdkSBrn?@E)1?jz&4- zm|yD_I%xeKC=yJ|+YNiGwSU9`%e%}U(=wY!;b6@xmm{0%;fEW(pr&Ml z(+kiiFcMGNwd5l51mY}-f?<*tX1V(Me7Iyl?%B)K{AO-PAWv&jKxR;B91#>3>2i07 zNJHu~mT-rpVnQ4MDhZhv3pBGj<48<8`Hg2zImfM3>{aw36zCFNT~Yup1lt1e=D?7B zWy#U8)teV)9uHmX>x2Oo6t+L%vxI_JyebRM7}jqTj}7VDT-TJxkS(5v;lk_QBb=ZA z8pZMa3DW-i7H}z$=C<0oh=@s<;kUwqsE+T7$n|;_gAbce-fF^p=^G(I*afLxY=OSP z-xChBE~dzZ2yZN=$1UQg7zzMnO^XF2CdSvcZ+(9KW8?dT0)CjqHkZb(5Brbl¥e1^BmD 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 vnFU> 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öHRr` z8axy)j&~NAk`c*^&xzU9NbI2IT=$+n7TP$^V;;MT zi`G1uuN^}3GQ7KC^vu>1q)dtg4wtAmLI(R~L;*eTjON|@0$=tB$9UJRuuCelnOD8y zZH_RrnYBW1Yf&H(fe4X>y9E=khrz<9J_ntlYND>@=||Q*f0MKPOaj|gjWe&985jhH z@ktEHzlw1#M{^L;XmOvvX;3Ai7QWGR*&Lrja4q4yxOX~0px8&gZ7*;$DwjnyrZP7d zV=r-!(ul8h?dT!?Bji$#6fq~QKSB(W23l)3G(AvhW_461CXv{#v9;uM?t!eD6@JTI zDrl~7B)C%0e~%IAZM$#qAb6Lu*ViPFOYqIPvE+2Fy*V#p6>r@w;i^G04 zh*0>d-gsD-l)^T}(r4X-QLEA!5MUp12!0uB6%k{>Cka4PFK6j~k(-r7-z@%4wkL??0hR!2wrXkccpE4FlV8#Xs(HQG~I| zA@%JZf7KtjsZPv4UZ)CS8YHBfHK1ZI>rYO4CPta2;3L4VzjOfFTJQ1jh_(A%m$Xv@ z+=m&3T#lrt?p=0S%G36%7jD)t-*Lw-4fzH%e#8kg6pdHg?d0#UvlS` zxMgH{XO?WmSoQ_BC2o47A?tec$_?P~`TMLDe@A+hIE}HXY=z$R`7Dn=kqzbs$(N3) zj*CWEcL9VzQWv-%9%!GQnHkO*Q;->lU(vDTr(r(ul&aJ5_54)muI65jUJe(Jpd1@wGypUc`4s+JtJ4xN}q zI-%t6L*WocCbfR6gHL1;ZC6&X>!Vu@Kb9Z8amo2vXf4NV)(34207Se`G~*p*e+wm4 z-s+tD2(skUTnRvtTYR-)wk>#)o`>62s`w&L|Mm(v=K3)a^NIKBTSM>2TIpEE_k~|J zH_xlSU2aHCZ0QAYCq7vCzEHe-*+=kLp~S=VBKW!m`=efO8HG+K`!Py3JC!^HJdM@Y zpevJn3?r;6{$vd4p!go=q2$j|f2Aji{E-&o@zcy>+qdRwYl_HJ5+WtqhrWMB+7a)RP3HA7|vvXG-Kl9e7-bgZQSo!uUfBg-Cb(r&^ zw*M=`{#T6-?xgOVZ^Jy4PYGb)=4i|z0vi=mO zn9klY8k6=wCxmKI>h84HZnn}|>B983DeKE?pG;FNE}Kp+yN+_I^S3o^Mer)Yk!93T z!~yt+sz8D7ZYE;>|btxmVfj;wfd0v-;FuOml^4OJBzo}+=OPpTnlHHe@ zv;!GKc!H(>Oc$F;e;HfCAxps*HlCT0*V_iJMLFCMx`$p1iTGX@CoyAA*)4~Tke90Z zaG~#MT2_ZUf5%L4byky9vx15GuJv>E zGQ|*|+I0RA7}(u8=z$4_P+pmycKWb=>R*~tew#TvLlNHy(%pQj)ok_-te3rem$+8x zV~XTYi;y9q%In|qH!>MMwhVu2dFqxHATGPUz9A&FNtt+oX*>z|jekR#aeI)Pw zV3UT7LuOcQah!)l&oY30NJ*;C17}ge8KNFPe{aywE_pWFyol;ny6)Bq>L{r`k0g|* zQO^*sV37~q>o0e=lcx&=w!$GHA`|`}DTV};3T19&mqj%L4;eB!ATS_rVrn2fJPI#N zWo~D5XfYr$HZeJuUv2{=laN{}e~oqpR1@y^HzhGTq>)J|y^RLxl$&%S?sLX@Ti1YJ-X3KO zQ$Zoo{15@KBv4UHUknHa3kiV1f&flVLpa(M_7@7^G=_P2!cj=c|D{*-e}F;J7)%L@ z#+YfLkU(`WS0F?P2oaTph)IIMKtV8A;vYklha^x5>J7ICY6$?M9Q5)xv3zukfI2$%=l4vGY7LD5by1SX>$)D>uevV+6We*Y1IOUen2 zc9R5we0+Qapa@R^l!v1Xe>We{2aa|E>cc!?9^No};IDFl+E4`ScVPkmPN1O^-19Hk z0Of%8fqKAz7{C>72Sa*d9K4YBFb^OmH_$*$6R6_`L;hu~`IiA7@bA_DAp($p!~O03 zD-ay{+Zk$SheEhPk$!NbBhUfv3Ipn>XbPZx(R@HC(*Bnr)YTJ(fANQUL*cGaTa3Z) zi$j4b@_IlhroO+c^R)ATyP-V=JmIdtDg^xsgK4rd(q0jTK){e_Pr$GHDZxEpc9^dF zfqswG1&Q)O2K;q)fFtc4ewASFiH?C`4u8p*$$P+kfe*kK;z5AmU!Q+ZmKbi?qmZtCf6RXm7o;t( zsjPgT_jkensuUDZzQ6!}K`|h|pa>WUfd~o$#W07U|BUi)fBpZ|{u|YW!vDS-_)kPN zqyq{l@mC3$-u+XC_uri0`g>itf&ZC78-?L949N9|_7-3fupQzDff&_WS#SHHjY7#4e>F&m+U!t4d|zfDbGe=V^V%pUHA_;0Nm z8j9Hrd8DK3f4@zHd#b>FVfMOkw4Kvm1o~$NnDDM}Bup3O3IFxX0Qn(c@PFwryJqKt zc}hGnllrZKVOHrsQz|3vQ1-uePEbS?2=(xQ`T;P5$4DZ;00?F|?P0#Zc?|>!AW>+H z3lLLY5YPeT0r)jf2oR(Mb45dcp}zr92_Q)MU+LGCe?azslo)fEyBB6g{~#iQK#&8< z%j17}2m?V*|BAwxaG1yNkBo`uhIxXJt}qAm9~AO8`WILJg@rJ%tC#1WY?#=dm{s{V z8AKEa`eTnF7YOa-0sE5<(>4&=2lXeSI1uCy^Y{z+k4D;gd0_IRe-jQl&dnb%=h5zUMn;gr{VFSL$>Uf)W$+Ef z$2DcHBe{9j%Yg0XI)W!HNqVj4ffv?^`V;G|fZ1`{w_`82JNt0s@oADQdT`u6*8^5;64Ws;uaH69{ed+Hrs7@X?G4l zBSeH+hNQ3Hg2+CJPu;4#{xK5+9^b*3e`T0eKEQvIj*js?4-?U0J#VCoQ|Tm*vyRVJ z`$zX39HIl4JA6qS*0LcP!&ifmcDLA)kg~@`qthmlN!-~e};=Y z!8}hos`bd>XO9M3gt=An5>KhUo8BEi578wt-diuBolWR+gS#X)**Nb{AAGjy%B=5J zE;r&v5$=7hH#s17R?v2M+P%=#6s|jH7&D9CQuH2b&9VQ5qLOsbOUa%lVpb?lkMKB6 zfx+Cs!Ou&WWGlPG_sw!q%gWM(fA*==RZa|Kmg)U!p6QB9iWuFbT4u8hC)34N(BwnF z?A?WR!;EY0(%_pMYMi6j?#s_~)!1wKwWyNDwv2sUM#QC!lO+yjvD&z_1RBOV%IcdArze^7>f7NrPKAe~#>QUn%$p z^fBf;U`a2XG}uF;zkX7Eem9^8@}W!4epD&g&(xzMMX5^1JfTE+P+%pShL}b`kq63=S;}1UIOd_2UG=^frr8jfR?tA0X`G&wA8x zn$O-bhmrbg%xbY9UbBuAf4(lvI=Gl6;4_&GHvX*%>@9dg3s`<*} z41PDM%XvvKa5gRDJFobrtaDpr0xvxI$Np`fyh*DsNhGHnM;gIAKA|nLSJ*#jUvBNOr`F|0;7OD*-unTl!;`~ zadK6Ple~?-&%KSk9SV`o?PQy6(LDy~HVGD1BzmXIl%H|mV=e0SXv&Gq=W=pZ;X8z?eX<^f7J;IfC1e=`n%pp- zH%0V`AA8aje|SBn6cX$<7qWAO9(Q{KGSilC*HuV@1H2eC0k?Yc$K`JkEI0=RxC@zB zDPWb}eesFwlA7ME5Np5*37#?O)!&LYSdqo@qB_Nz?(?HFLN=cC3uqh;$0EMw~?Llxy=Dt+0_}We61Of$bd}c z8GT=bwlG7B%hCoH4VDjeX0xjhSnHIc47W39VPnNY87rEh)&p?8g;Tdgm~rHGxjTa1 z_4`D`f4GF&C6_q`UkNSZ`lT1WvLD$?hf(o5$Ws zYGY^2Jqz)D5wGCA^#_uyysC;Jl0Rxxc-gcm0OOO454WF~rz05ByTg ze_rY8GUHzA`}iigy5q}2hk@rfxo(IIFk5S3hmpP5X2XXLhJgphrrG-`0!8TdS1Rv1 zf;^@h{F=>za|`O|9p>xX$EP`O4uqfhvmQNG)mqjIo|2B!9oknKbH)1nLzB|bt>5ge zoBG-@2}7i{-J;$L$-N~k=cF%dB}(4ek|Vq2 zK8}fdeb&BtQ%T}lYsmBNA>`y_t|IP>t$jbX>?p$R8fSLi{?+~gi;S6oq>uxDM91A(ssFVrl}?JG93!1eX0jtzTbbq=|z!2PGUf7&DH zVu*RZmN0f?`uQBtUQ$q!XylNO-9F5pCIeS(Z3~^?x_jfL38++AvzW@K=BWf7KNT@i z@Uz092#PV0nVJ;z*GX~Tz$-3(=w2OO5W~cl4Sam%p*9#i_>q;PSjKaMU~%`T0RINK z+DxwGv}K?~r^ zHK5zkdXO);;ryq&fN~%Zi5ebDGZeiCmo<&H9TpD%^dkuU6PDd|g-ee!HGkcxIR**d zzLTVBZ$1+ALomYRdnTp8ws3J=xWHbk73T;~;W$ez?U2%3;J&$R?d~J7e@Ag9kOS;W z5*|_|Dcv>5khA4!)H1QZdI$AMO7188ug_#yzR@T#=E&+=U+<&GQF$ZZHKJ1zG^6j@ zl|>&lvS1x85`AP|dXoDTK~H0VFGJJK{~~6VJuoqjIB?>+Lg}<3bvDL8Ku9ExU zY6fK6VMVBfXjfuktvD0j&3qe^s~0_$h;BY+J{4s8U^tHq-V@j@4+lQ#9AZidEVJ&r zMSVcOl+bkLGR9S@a*Y5W*Ti|UFPP1+gihH69I=AG2pK5MR@@#*e>)uUToi93;vHT4 z6hWGr0#9$6`=TV)JV>i_(Ry0-oqhe)(LxtW`CLmEJPCFyMV1XeRkjH2ovS+Mon;YA zeP>PFs`r!7KU}{zigb(~u7px}mL(mSn~UVY6|}u++>_a1!un&so<{$`>)^T7PP1j5 zm>_#ZW#^NF3V^Q|f6aQMfii8YNOeJqPWJ5hzR^2LSyZJvNiBtm1^Lyxo8Q)1bKIkG zisjUeNoZzhZ8 zq=5ZqtCInhzI^SDj9~8~emJ4dw5%&RoBJXc`GnF}orot#e_k#}xWd*!tPB0w?4=}F zg*Muqk!N7-a)L05R%^xeia7 zhD!V(1kMCS-&~PtnzcV|&Kyq1XhX`v=qqP}N6Nffqt#zK`zVdkLnICcG^|X-G_SF_ z>fd*|+}{%Sf9QKix1$J(q3y@FCVX7}Wn%3%HA(xU2#8~kqYbd}z3sOf4PL244d<)I z_9(+<>vCm|h~fy~1eK<_;&!O*SF&Y_(f9;9#OGaI9vh20_RuiYoFfBJVQVg0q zA_7>Se_$B6in5hnMV(B<(KeOX#b!l8uK*h!x@PVTjV#n}9GGtA%nBL7in8@You3hS zQS3$Ct|v__LO)zL)8u1U{(9vdJ~VULBrY35E;laqh4LC9Te{!#==tiKrpw7rv%NIB zF0pAVBk+^{Z(24TbEbTAgU2Ww|DxwaBn#S>e-R~DAJ6g06b%mY-E3!uCOZeNEe6H{ zB2WA11^FOL1%`Lj_4gIM?hsvqlw#SdVI8AMfkWp(x75!ceOaC4N4=oW^w$|RGfY*q z+<;H@gU;T5JqKzqSk)Ij(Zh!hDBgnS1;pUr!(Mq=N=5LCz*YZt?=W_)ZOTqdn)6%RA-96-M#E!k*w(}EwI12 zLu>kE3>m1_c4RqHQsCUj4uAwz&Ky=P+mbV?YpoyhDCS9TL;}zUU~yPQ;I=Yp%T5)`0|%{5`w35b8zGiP+7gh!tP%*;|xuG!4+{+ZMZBtm94{*v&si(hNLke{etD zM3E8^d6_S0b>Mj`)>!9Xfea&1lKyQ$8Z@+`e?*G14Zz~K<7vfX9e27_3wr9bVDX7|pMwr@$J|Lf zl;9pgZ3BCqysD^zYy)dwKZxoYW_6q9P7&_K58f#2(w(t1vN`#7?C$$T-Ft=)$q#`P zy(+afhhZ%#Rc^p}H)^t5u@a4FK4_`kLa`sg?6cJAiu`$JpEbb6Edk2&f4LEn4dP++ zN2kQV{VZW)|X09eu58H@h*#glg+S(=tR{ipWo3F~|GU z^H%e&K$UNj3?xAYaF>twWfkQQZ0m0l`I_E^z=lOv3qvL)^fWi~Zn#2qRcw^vEF%em zvTv@M|4gjmu80?R3oCl?e}v5EOVbDsS2O(WY->i#p~T<){O6vmGVxN?VE*G=`;WbtS^%jC zCnap{MMyV`R-j&&RL~uszMLkjVsV%nu}(@b+wKON-Y?}8uIANRf4Gl}_SvNej>mLA zohIZ8I&}^C-aFq04OS?)wDPq}6ha2@mIS2Rj(tx7_mn)Zf=0xvQA0jE7p+;)Qc&SK zH4_<4I1?4##~$h}jZaq74AX3#Q*lww-ir=Z|gMwr$(CZ96w- zocnlQYSc?T);Fs5UUSX#){seuy)E|=9!`xq{&tljLLt~HF%0$U*t?YzR1}G@1|%`_ zOCvsA7ztQzbpYG4HS5_oRSSE$mp7gwu+)4wJS@*ECV(7}gzOw5!>jLxxK;9Sus z$Ttb2M&TM zYSdGZ^s=W{O(R($HLSx$HXHk2DB`pckjX7FQ*xRx@PJFQ^aZZu#x;x62gf?^p)egb zHU1un)UBGw2707u@nhneagw;a%Pub1H3(jBqITiBtT`YC!`c4i^IyZY6WnEnGiZ)f zU|tNX@cVy0k2+^kjUX^PdU!JSFD2T77e+MV%;sP(i=s;Raw(h5ccTMi0&cp7E_tQK zH;Sw6uK?KiIxT>gO)x8@`ycb$OYXm9_ZENZ{weq2CeNYVegsIYl8`teaIsRe`?7LF z!C_+w(C8apis)d@Ot(lOi`3GNlV7#}rUm5EmHGt0qk8?VCR)M-a`EZQh{Rh{o;(AH zXuFKmQcS8x5gbi;QKuTPnmilRf2SPa)}r&$Ljg_?qR6MZJTnZp>B7hfistt6T7Wf% z{3go6S4^#E`sV>(HdJkM_Yl9sW&+6b&-bnP-qCP~@e*(CIhs?CHy!qR z;|aK*B;$J}$+ho|4Zj~+c@hJg3%tadhm18iHjL4evK>sjoPQxcEE`7NCSGkk;V?EDaLv7R_lLXNIHG z{y}b9az_vbr=e!D;_{-z(K91S)ydv|t|UYj9*|I%I& zfeDBPZi!%na{()WNCILKPgn4sN1XStqqqE+afW%`Qhuh<8_{_iOxCwdb$V7G)XZU~ z7iU6P9l80w$Hf(x4KE;g|F{X_@559e3frjB8}mz7e%ht(^KX3n`m@WJaXrBE9ab{3 zPJQ7{yYCJ(l|#Aby&4x|&f>0EmHpp>41edVI%4%Kpzuf@OAN+w=)*y8{DPE6qrR zB=WMoiMx2f{)Hjx`-~1-g3!Z2R9AUVJfD}rahfV|?S&%CWWsq=QMf>X+n%-r}sm7sCqSO*lJwF;SH;yP9Y+?LLZ{gr3DY-DzwQk6RXBxtAtWQ&E zz`lIiSAHU`SXo*lO$lJrqm0VTiALQ9n%|6*yudV?Aq;y(<~5J&no}!sPwf(}(_8!* z0;HUe)as*CVL3^iD9W;4B|^F$Fy(Lc7<tpTA;;NdQ=%A0PkSpx;E zS}y(l%G?TMQ+tSLXLU4FjLI(2?Q?l=4IinLhMzgI{yc9(XTLlkpRF$sn@hYdA3ar) zDMv4DUzPgHQm#;Akz$Z5>F6-s8hRxM27KcsML*?@^h}R5{>To>H@k|@zR3Vs=VqR3 zdwA^CJOrz=-T`jHakoQu&+9qXEv^TaH9x=k6&l@e?-9(ck!aX=O$K4ZsH47cu1jJ@ z=Loj$WSQDlTAdUerbL>hklZr7AMW8R|*-_8;H1K=X7pz?~t^uEWI<^ zhz~dlE5GYXbC(xH158-4pK0e5-PpRT?n6p9+Q3;A zhHTiHcCh*Mb67(X+e&q@r~89H?()cw={6qQNQB*13DU!>8Cm4K>A zAFq`iy8c%&3?gr%UgA~Y4vW+_JTY$A($KGY3yIV1cypZItT$pzRcI4&w zp{j03D)hg`@t|W7Hw~52c*dMd=eR)jBE89FOgZUlJYmFEoKleomO^QaPIrN1Nm{N# z6$60+w^gxB6=<%I(9B(Vs1ioiUPIY@-yE{4_d#!N(p?Xzl~qH1VDo=j;cWzq>M=`; zk^p_S(;td^?COEGZk$mMQtMY!VlbU7eOb-fM2|9HGf<|6$4Pw=-YC*r#{VvWVs2#K zi)6%gZk$dEkEcuLx>Vb*Py&aTT&P)qW{uc;6AuOxb)#$yV}HzC9@?EaHJw z<8950&9lzql1CZ$4wtK#g^N0ppMw7rw;r7~3@(cwA zv6;8J`prWNW_L~L()kXSOQfSin~*H`jTJA$&Zha=kTPy`PjK*bLV*7L+dcM355RzW zfdq}VsrX*;LSiemq_OOYmdrzT-~WwOMB3TZYuc>Nm4|!z726+QtK5VHs>sFiSxEP3 z(aRMJqwS+}<=;L8pI@U`q5&EA(A+1J%9G{aB{^STpno-svgSgk;^r?3=p-LZl1+zn z&gF;}%pOQE>V~(k<5FjcQCww=`hcaE##~TgUs=2^TUR?J=CULuLc2JJ?7UcX6&l{h z(u#D&Vi5f|=ac*6N^F$Oc>IS06=at2kE9%wTCMd|pM*>h}ea+Wd z%%6;-FOvrFH(S>PUm%-rOVw)!`I!lV%=KWDy@lcB4thwDrRjS)P2k_#YjBa&GEZh9 z3PZ|*B@b2Aez<|!L)RvmY=E0_X0>C{X!&hMzXn@Qp9m5>sjA&u1b4_W@TXgv?C}Ke znX!}DIjdVvZg#>ct60`uDQ~XG>`i;kH=*}a0n03p-H|{6Wu|4-Bo)Cys5n9Ds!!qE zXP8%+ABUrpxI+F8?i}#m+_Jc~?369{70;zN;UE!d+qsupk>37oJfL(kyvW@{F2$_o zJv=>XVWrs;GGq5>p?nlVbibTSVtXhU?{yQa$Yc;JVWeG zw)~cr6PGrRT00qfZ@`VVBNdQsnEK!pB~tWXM>*F)=TdDUnh@$C?PHDL8l%3Drp0&9 z1RoLP#*a!1WBWv9RvF%$uu87@EX3){XnCm2%`uMFpmq3TU!=owvLbbhD}=}9$A?7a zv|v2^o-Xo;0J16la7JZ%VJNCt($@pe?dPcW=ueWUl-c*sRls~ZzJzfc_IjNzMC!>4 z=Bfzlufx)lscufd?jK5)eU;x_`cNk^kNokxDjrC-f74pH9CbHU5+C}wzIqq*%{z>P z9$oGV>u9QxOG)<0sF3C&=#ys2CJp$lQlau?{dBo*Q{-w@Tr%ch9uJPPKVhjgg$BMi zHhnRFKwj(AX8_A|kSgCe`GJxBh8|F2GqetP%9E3SE26)wyDq$u@UhC;78%aN11dku zMp{VIy;^QygwQCH0P(f~c?HG3+z}OMny)!|(sJ`@jUqf?215vy?8CHDlsJHw`=f?b zdDichUM3Zz+O+_Ku$z~A{7(8Zu#o<(iAP1p-*!spP{UX}jl*F)s&ort!qJJSbf!@b z`%`epaAJ+Bsy9v|&dEM^lj#A%%HlQJ1td@EMwAXPgL_|}(C;_#$?2d#WBRlcb$o|` zE_)$EYrbIGSTLc;h5QBi%;9A4Ukx)eAzWObdN)Y}as(hSjeL54O1Vkd!TcbWcY1kRENZ}>hhsftu0FrZp2Ny=mz%cA zJua)>%qSOG)CVUoq`h-L3a)yZ114$?qKX}fm+zv!M;(N2&VBS(_Uj)d|VmTwF|?Tv>{pgg3YiiUr>R{IahDGXui)Ma0O# z+CgXoLNnCd$sSmdGXvI0A{g$TNOL}tsTx8g*csFVWdirZ)kV(eHQ|TaF}Cq3>OkgS z#Dn^xRy(Z*LhsoY1Kly&|0v$rUIn0n_w;6X0-M=WL%A9nSd z;@*B_PLElfOH-3zWfbW!`m2B+G3Ru=w7ve@t#V0^OSR@$*TMmDW#R+jm&w%Ag-tI3 zq-jpa!Zs2;SpvWcou3?t9ug9=n}8hX1PG`tRio}(UTb_4fcH{-FMBWfy_NOk`~-B5 z@r&OkhU4ZCKdd;v76x$#%nricvSe(C@GzWu%K zXSwnH)LpW&dTLCRaH(?y^3k1p?E`?TWtxFKuzc?Z=(v&ZCyydRYOZ`Zgb;{f#bD1! z!5tX=drkKFa)6{iwBpyxNy{WYi8h7k85`}r4fWn_9`GktCcXVL zfSh-1Vqki#zpxJ=YBWE7HrDE25N^O1IomsunoHfi$z7J-{`8TnUoafHYd^au-L;x` zxUL5MZ+e(6j0YmwcECHjC$z0?lLJUv>dFcLXfpH5&i4M}{6+HW8{NxJ;OFU0t}h7P zKd(G5ff4sMH-Qo3|GAuZ$D9X7tpDfsJ^Q8`%-{^xw}U$k0Dlwi{^kGend{5XmP0w# zmyua&8>4I`w=Uht9!h;al`_I|In~Z0sxl4!xBVU$!hp$8S*7RqfdseP3H1`-E{UbT zaD`|v|2X={ePAX@_;Jaj8ZzHCax_yV;rge9em{_J;QVbQMqIsdz_}9-x1-^+Qa?SH z3(f2vCj*>e;m*!R%foo{>}@PrRPe}o!KM5KRUUt=SS^2B^z=P>V#b?gG>f!A(AI!d z$e@qIp+BRwBW-9D7f>V@Vk!!_H&m=X&->i||YgjeCS&^S@@x}RID#*vhFd!f@3LcD)j?l1(m8|*c z*yZ0D<<(SCbBO;Q+_u~s$x8!rS83d>9%MMS$`_U37XKHLKon8j) zt4z<4Ugsh9ZocDD9iOa|g9hkQJQIx%^B%&Xp1ydj+NUF8>vI_8B0)5}vN|{Bz89Dl zI>zaO=Fg_F-91E+m6KG0-}82rl0fPF%dB}C1*0k|dy&qj9u$+LqqiU_YkY5ZFTrf2 z*$*FvBdulJ)Kh@MVrc?w#H+XAV5J@|5d}6|=5{f4B5!&SCojsNaSE0rZ&K0l+t_!7 zkc9hSSFXozB$_elHAPuNapsj;a$2;8fFfZV0$0c3-2e7aWdkwBvs*>Mk1N_EYv54pThbLstVtfD!`bslgo>gGx28 zPrf#reuRm6+f7Cz57VQ&S^zU-J-yA2yAMpoo^IUSjzBG)*@Dr?``uu0(?*=Ti7zL7 zK$vKsdhG}+rPC)sNz+;{{IP)7-UGKV3S#EDx-qW>8MCSJ_tA;gFDt#6CnNV&7S6@p zzD7W3jOBZ!&a(lKVg6xz41$^nXzDUis=D|U^=s8V63nn20z;dMyDk}7HqA%ZAh zb9I9UxBCVYf=kW43Y+mwc?=tdk7C9G4}Zm)h4pd4C({dC*3|YVe#4M-)kQyXTq#ul zJsi^B;J*B{l}d!#xvS>TUjK;)R!Hq4KJNt+vyI|Vl&dkA35r>Q2#T0u?nY?{2d)n! z*opm$B2`)fP#JMK(is-=rvy8n)abD|HcrE&S?RJmB^B&ERsgg_1~p($GTDuto-_8B zI#@}NBl+I{u$WJw%_cB^#DBk0WvoqKzR)gs%eGrdO1&RKmJ`kY&=|i>bIHIW?K^&| zQ(_*r*3m1LR-+BoTC1bi{$QJ|!fwwm7SIO946RlI;-EOYD22Omugot)Y*qzIk!#Lw z2;#DJIAf#=8!a-uUr^(7f1wk+7U~OR645LsF-gTS`wpT_=k(yPdtNm%tVWWgC#u!) zd1O3-+H7jr);iN6UgMl($wqvtjqy^9O!WQhyIiArp}PvgAL8=(vW52X_4?>Hbk5I7 zZ74kfm^-#byi~sDCw*!s&wqF+O1;SD2OH64&G8s^Q4Y@q%poK%Az?eZBUXhp7174wajdaT$ns94L9Lr&GYRVF7L+a+>Ty@xPy-yKkXly;sh83r4oTL9fte z+Rx-d5XIebB$U?ZTlVMtfTY3RJ3Q>NCl12=sa*5c1SHYc_@M>1t3knKJxT6O>Ok}X zWvXs0w+b9&94l8SB30=Ml43?L3DNh6tpa-Gvj+!zc$_R*J7DW_tt*biZV=)JK<%4w zj({j`xKN$1I}h~M7^sZADBjmWHZ3d|vk1OBJdH8l?lYRQ>-*af$MC&#nx{r-STg>F z*0|ZIzin{|Do%<}twObNwY2bcMkI3ptbf6ue>u63epAjMvh$<2y2k9a9!9MA<6&89 z^fVA_al)c%v#hFP`_#==x!$$m^`lc?WEu@lkvF~(T6a;t6bilf9_$(7GDQJJEPckQ zSE3Oj9QrzIO0NB>c|u0cJ!i@v2U(!Y`sIsq$UhsX&f&dGt{Gx`e%E277=7*lu$}8h zRH+<>k^lAR%s-nvHoh5w-HWk6kBh%v5DdyxKUpD28zJa)Sld;7A3ADe#v@j z?J+sVJT}Jk!3?v=t#&kaxrr<4_+I=(Kl>CV&6zOk313kwFqZN_-V%-fZh)~|!VdYS zKBbAX8PqjmfnLyO{A9AZW5%Zlz{1nKy|y(K2RO`%{*^WL8DD1NmhUi_Y(|fK<8Vx4 z%K{0=i1pBf+~+Cd4gfhVy+tnNS(BzYx-fV zLV2(5TvS6Xk?sE7brN#;?`%`X2e<>dE-kaeX3Z+Xh&s*rlZLAXCsW!Yw8yrFc<#Py zM&M+-WfoGwUZ03GFU8i1hk!-c`%EbRhVO8`N$Sbk~&VaEBDcd5$#B$=#Ibe z&1EPgZfZVFg4CBFlPXx?Zce-G@J89CA{gNCm}ucnjTi<8{Kl*Rf&lZha+2L5cg~YI zBGuXcFu02J-c28?ku+}nQdEyZy1U%$ccz$Q*Nmiwtzm;M_)TauuoERSB@u0QX zOE_PZjTUlZ3VSHP%92#Dao%zZ%4qKm${D78obR8zC)Gt0Qf|UK%^vrOm{5$@RxBa1 ziGuX?-?%?zMHs%M78u|iqOnn_$p%Qp93T)pYv_TnbEI`ghWl5HSrXSy{KtH}=K&kD1USjllWt zEKePY9GHyGD_e8;7Q>}X+OAT3)*NRN{3kJk%oR;42S_TwikvZ$*PEuL=(44}UxzA8 zy;s!8?%>^k#f(v)-7qw5f#)2Gv#?DJ4QeYn(B% zJKkRIc_U*9ffK!BTkBj>Z`J7#I-H=QwME-$6L=t?gPR3!o$^&H{lu-$LJ$M4vQ9&;9R>Sbn`rHha_9-p4zBI>C`)RV&kV#KnOh`!r}xjSQM|q`}gjel~H%_!6&& zf+2&7rqJuY@fe9&nkd=#c_NL)!OFkXE*7f*4alliL-z8MwDtjq*=x&f@gn!7bIBw{ z$3=x3B|q zUu9r+wFh}nS51DeN^e4j$a)b~G8jxv!BqAt`gfuj4z_GXO66!){;{}VR> zc)QI=!3qUwC`n+|u>yxLtt4Z$Z{O-nqlkck=c&Fl8Uaz^kge|4{)R?YX%#s(d}sFL zMA|*JwSy7I*)Oa2Fl6=WY^;AtMY&?Un)=ipiUn&DQmtT18uwO!cSb()=6Z4l4Y@i0 zx`Q9U8Y$x&VPue|{1V_WJygV_J(CUw{JeaMDN6?o2ysyd-r(Wq=r-@{=pG)`<7}EV zUcK468KCTuXJ59^mOften@nX%B~H=AKUNHk2{* zUiDXtXYhUG9W_XRWoL2hm;KGVu(S*$NSRb?r+aRhmdCE85hH=mgL$FaYO7R$2P#82 z7p#|l0bGzgR>7Q+Es9fggET9w+0ie&U1H6kgOb$8hf$}-zYM04C%=8%t7caa_jCUE z`SyIoKeJ;x5VlF<)8jdbgBFw1`c1mWZ2yv}w{i38NLj+OS!U`ZRLdnUVjQa=fI{Du z>VEFQjn7Qpw8dT#GYp0M+HTct4dQA4A;%h&iy`vq0Jbt`qJKqah3_04V z#8C%)`73*gObrJlhh5^2l?P{b5B1Q?fatAO*JWI5MHXh}IjjLK{7LZM0zS6OmSe+n zNXyN~^Q-I%4l)WvN|<2=W~FgaW6WP2p}my|%%??42)}siR!vbEG>Fm#JhpB!%4PVr zK@n@or{CBpi>76;#dqiH##&@*5eXh~-n+t|JXGfG4lKK|9}EV+=qAUt zE@lUOcqSI%$~JKz(*gt*a0ffvtA^#&Fs`CkU8md`UFyj&%VnXmf<_@u#=UQvmzN)K zhmAd6)zcbUl>d54>EWS(L;coUgiGCAoWaN-kuo(-Dvae{1AbdnPA>LM1u3M6BHjih zGNK)*;7TF@_$O3nu2OXUtAq#A z2`IuD33wBDbJkl>WXSPV>DRgMa86$g^OA!ueEQcei9axQZlmWI)nTNOFco?Hft=`R z8n=uy4surLJP*mjuOXJlOrhb%`0SD_?vTAk|J5PKUXSH?>WYZBH)GGKFMgBSyUQMg ztwBXSlk+W;gM9NrfYbx@5#%tsm`RX_+3DDUfXor%;ecWGzcpO%qGu|56QR2?j}@{!d|+MJ-YGvH@#MSAMcC1v zBDQ*04+nyzLOEo%XMxE*u;;t^d)u`GG(@}DRZ>1)m;bzd0j{4x&))l#EMd`>m+{pv z20->GsaTM;%xn%EgBb;-fY-t+mLa2q2n^F=;cDL- zI+K1;yfzg~H>dFe7qNpd!1``0`MijhH7IFb*)HNWaGR|i#2v)V8vpi_pfq{;Sx0&Pxh6g)nGqGk$#JO+q>a3EKVu=ptW+|txs7h7gD@3GIH%NBG>jzOObtYHs58PK z;ISlk!1Oa6AtW6RmMmpq#Qbi^5F~s(XD+1;pGhsDQqdu3o7F_;Of=g(8;Mz6E+*Vg zz3t8u7xm*A)%vrD>$c@NfVvDCD_$6iBAtnDl^ zsmo}LRn}xQ>LqQpbtd{gAk|egFdmJ9yVANOysdusGl4V}sos%xGV+pkZ!Kof zW#Xp?gy-oIL>Et+p>;aWQKw&)?=>cBLJP3)hF2r$NlS5F?;~LqO6foqH&1yxmzUfC zS~mC>f2XEV#j_w(&OT6Nbhml1(_!~yr`F?g-dv@cf8}FjUGSa5L5GuA$UJWPc_u+t%s^G7TQT&caz_JFnNM^&Ih@q5> zV-GMiR9tz?@gSbwmg8EnN*ly9D(^Z42qNC<8Gk%{l=enk6s_!KMq;cWh?_T|zaA$^ zRWxiU6?H|!@r*JLDarLQ=-dxY&(~(MAHUOfH5>QrbwO^UtWG22?hLI|hm8^Mt|F8f zJy&PjHF$U=pU`f)GVfS7I1qdSa45cb znL_@Bue9N7iO#dFB=+*WBg{j=u2i){hL0HYXhZVFEf?hp*Y0R|pWS@TYSpR*P|oxH z5iBa6rd-KS?wl3sUz>hj>hUKXE)t7*r`F~v(}!In?>I4ie%EO$`sI}no%={P0Zzvm z+(OHjLE3tycgT0Fk3;x!po6Fh04pMA58IGqT)8f3PSiS-H!pF)O)%rMZ3~P>t-rvv zO+yGg9ffejY?+mhATxZ|I4)|uk>E?1%ja<&7+ERWD#TU_%BEfa_vji0saE9h_H-sX zP|Wxcue!_KX2enIj1&*!)sQtpPBM0I+eplLuHx~OIx6Iq2_z?VoV@pL z(h!+i@P>Jhw27F9+bK{m`NsQ?&@N@w{auVd$-KvljwbD{O}ubOrGC(o_`7S{Yw7-& zAKK$W|K*Rj{V7DeVU(c*FuTHy75)|)AgvrFex+NN3pFk~C zn()7;C~NY1Wq+(G;%rPX$K=GS5=A8KUm)3%I{MN6TkuTzl7Y2`EaL|d29MZ1@n0fSZ;T)m zgLCS5yrM5%gNyt&Kw1k&BNoz3x8%zltm@raBjohz?{v>CyMwjWgBT_BVcFc>Q@i4R zsss$r?;7mO?iISyDzv3*w9AL)E#$14re{R!a-wUQtgauM*hx z`D%yawCqDFFaEZO>ME2LLw{jOH~Y!i6ZOWedWg+ltg*L zC7NW?zKz;^OEm<2xK62|ne9!DCwZqr5`RJ&;WKQ4R==0i{G7%UdeDqu^_ve+vIBh@ zMU%b=r{oa3EHkKz%Qz*Ecca|RSMiQmc{8`=J1fry-!5-@v^a-c^{M2TMSPPRveL$$ zkV2?mYlX%mph~D#fd&v1p?B!H<}1_F6GN5PxNVDAD8!5Dh_)c}LGM2!qUTpZQCAw5aCy3Z%GkE$uP3#yNVC6A`S_cEW;YJ_1@K)3$ zFPg%iRhg}C?&1z#S^&Ceic)p_k@!R8(He{J0C;BJ#0JXFpYJE*4_ot?inNoFKCen7 z{VnOzOUtUBmg_C0#L??;odr)OZ9i)7rZ&MPizMk{40QdkehQ+;A7kx=a4^otT*2F4 zXHeAufYxV7*VN|i;mn99SzT5KKjrjXL79kMdWQa1S(G(P6x+G1^Lxf429L<)44VGjtG|GGeX; zj5iFf7i}&MI&pnbMH{_B6Q$SL|5hUbBubNh3)k+L}6K!8cGKW4Bap4@KDqYL8wfpw@0nA>5d9< zl=YY&Y_=AzitX~+cLJfk^n_OL^=-55yQbj7T!2!#QGhoUdp|bqUX*bmVmc=QLMM6q z5SRE0U8bul2Ehi#!3OB}AK8P{!SQ~__pwmUmy}dqY5A@{Rxy?zdvcoP80-+>OlzRq z+2su_k8f)_+Z>zG$u*8{#Xh+x)WHx0=qgP)lS^P=&=mvV-Yw?u7lpzJz14ppKsvikLENy{LW#dftts9h@#R#hDWtQ1%uA<2d% z(KwoynJ6yK1ut#UuOo-Eck|E?&Sn^I)V!5E?-Ze)9+F6EMxMN0)h?ga`D?MN4xQrQ zLb#sY?cEyL9(Xnf6e#xQ%Amx(rUVlPp+SjdnE`~ZdZvym6=IoIAz^_8@la7KR&m^P zRTVJ2v5KVuT=NTQ`A+44BIHnRIH~lYhB@vEGSBv(gI};?sFQc0D_Tx;^swbBc>AxI z9Ph+>%{O&~cIf_N1@60)HqG6Q+FQWWl7LB8uRgW2$U^epL~#AfA(n6WT9>q4Ch^=> z;hP)TM!CJ`xp8T0Qnz`!-X$R3c}2uZ45nYnH7^g07wy0yGL z;O-i8pH`d-bA5{4x&_2bY77=v7Ihs_Lwp{3Ye%U--xyka3}Rs(7aY@9brqUsVb0)` zzB?(1gIvZ&`sh;u3M`$S=v$nLWP|Fx`&t9Ip{$kQ30RQrr3@)@GX3|X9}QsOJR;HcCd2a zOgLNj$c|k2JJ;(cULub5OnJ3Wgpv(gEJwy4qS6BfjfnyQo5BijDPi`!Ky1?B`-=(} zTmoG#@dvepert6#wdWuc(xOnTIta|8Bv@v4y*x-GEExYD=$bW zUf`g|SY*q9hl{>)lr)D9oUI9&Ej{2{xW zt8C5|PU&vX5})3P^ULLtHq>#ukYtKVZwB7(5*xFD^9;AD2}=|s|MD28iL!!tWC{Y* zNOs~C#!VwRPbr2zDEX!%{@Tb@kwo1N*YeGrzOe(MBrp80nBL5e4ckT_YR#Qq>b}?6zI-p)z5TD_bwO+cV1(iculf1-yfp3?C z=DB8U_54RFr|bx;2p^B>t>@FQoL}zyj?oX$Ik>xV4ilMx>L|8D09OmV(F1q=T8g0k zE1!buL)*1Z&)~<{-IIWm@#XZX$gxhp@V=z5Ep_*yyUvn3jc#7ThkSxojD3$EV~HC- z%-r%1KqQtrPtmZ?WQM$L(!5pZ=h<13Cpq_F(J*-H%3ql)UC<;!(A=o9!tdq@BETYjpABOyf#Bh+qeO?0h`m`8EHA) zcW}3qUW}&aKf$nN72dJ_p8Gp{C8!>dY^I<;a%Un+LE}@U87wK9gu|F1JK{R`SsO2S zn?DeovNdfX&q#d}62>Z}{l-ynVc*vAy8oOeK0=X`(X-Edk;(Q?R~SPB7Ijj|PRnzG zalg*k&`ltJD0ssdb|db7ZZ9PwLNHVd_|i9NzTe|luy`f9QT@~`w2CLbv7-t=xH8T} z3jEEOEiU{lc}A^vq2BA*U+{^>_EzaM7aS^h;-v!?(&Qi)|+P&rJwG=hyZ`h zlvwNOROJZ&jcjg|ZM6CnZI*|~{LFe(`5$Okz{;o$8vNu8Ws$MU&C?)rT!CA+27$mj zE>te|nV4i6FeQcF$ul>a#6uWh8Xh^jIlfX4_PW%WvwXbK?IK-YzFXdCx(bP*exjwu z-<&t4I@MFS`u1>+crNW`gS+OlhL>2$!GX69>ZCuYydc1WLCDN`t^a2!9BGz>EY4mH|q2@q?rw z&bu{xT1dlXbH8u>Et)iL$>(M(T6pe7rXkMI*g2JHeMSbc3;(hPRy|W>5Luga*UHw! znC1MKPx6Mv75T~CYd*Z(NnqQtBK}#zzu~I;ULW^)s!>c}YKOy&ZT%!cw)U~PFE@}f zFDK#{$m{z+YdCIoIpP99E0Tly^)IpCX~@&ds=9B1)56CQnKp#!@9frGqUa#0DfRmm z!`f1EF)~!Gy+KQ!rHgm~sp#~WF)?P^FaZ}D-|3nQP5R|#YUOZyrss8Xt0@P8p&3^i z1qq=Iy^7ig>Mpdq*mz3lju&*uzMSiB6EZ zw5C<5ijoRSh1VaHq%jbw;$0f%XCC;3NOVhiYgo@vc#m>GLnI+AieS?zK~q6n^(WEu zNj>KO1=ACh%+Lh_{G8lqY^)BWez{T|8qF@7B3}_v;Sy&~GJHcF=?No)ZSKffnxOG|X7p+p`bhaR5cFc(IC!<5RXj zi-0TDr@k=&6`{^04mLZq7(tBiMLwQseK&JEh6-j4Zxr>X?|x-a8mhRXp{un@k@L>g zz+xZG`860d+{HN}74Zj44rerb0~(Xu0F{SFaa4;Otozu4F+RNiHjG= z@$6wOG@T!$y(2zlWzK6iD9m5mjJy?7S>;bDl$BqARj8<`wYaPAzv$JDOZMU3Y_5r^ zO}d#wT3n}QBvIH)jWttbW)B;xl z(;zP%;r$RL)2l)}*&;~S9AniznaLS~3x7vxyQ3yVU@Y&CC`@{`y5|V_YuEaA5JnwU zBK-nXQ$nWgEbn|AqmGHuq8uxAFuG9HcTTy;1L!&}L`Pzhe)3<78Go6Zd|uV90EgaQ zt7fZyh|a46Ca)@gn;Wf_wQM2L8K>PSD`Wu90Y)u%+~LXZG3vp~FX!H~7f@1`STG{< z*$y=W(bJd>h8&Ni?#Wr<&=Q6L&nDBesowwc>L%f7OXo-{V|v_!<@*`=y{u`^nrtXw zG9s^{$n0M%=>S`q854ou<&F@?eJGGu$8>sCjm-Y=8{Qjjoo z^8=hrcc&b^CyrR!c5N7R3&sxCF$mkXMCi!X7)Xb(m4XJ2wK6GBGp?TSQT4z*^2YE&Yf-e+~y z#y1@{2}Oh0i={g}HalKIEK9R<<)1tKxSQH}_1cAI%E5|m%_eY&n`hUbAZeOmCh{0V zd4IRStE&4;s|Mneb_kTo06^XsJ@)y>AhnRjfjEte4tFMDILCS@_4a#nWA{luX*j`K zL1T)FSVz(ZaLtd8GY!E_u6tsBtNfBqk$MQ84PNkppr#Ffu1y3cJstJB#d@k8xrU^2 zS%t9*Z$({3eLXW1835HrcFaR;!zKU*5q^X4RhE(zw3^TMw!b7D2RN-fULDmyJQ7A8 z7~Sw?y>`&Py6Y2xNU;|6AYE@;jxC{yljKbP>u}7m3&kLr+^-I*YT?D??DZ#O!{wdg zW9=BoR}*b3%ecu^z^uROU4)EQgeBpMtv542@5Gvp!#a@RYl^d{(cAjSlM1?yZBLVP zX_3?X{2VW_TrSg|0npoBVDUiiKs~^73wp|x^+8fpMbDwosWn4vV}9b4F2YO)z18+d zGWl;|ZWcor6SqPe_7?<22)f<#$l=eS{TfOkhhIF=v4^a>1r<3Odt&i1-St*tcG~6R zZD!*O6R1j^P0S}4zeqz`JE({J7X3-QAtfOtpfvoVi85es*$a}c z&uyC0!47%ORL;MIMWGzhB=7|{MNdN->^wAw^84YlQ>j5%ltGRJ$M zr-E%)F&}^1#cZ#=jPPyA5Mh?mbkY|@8ok$}EmK#n@lT~Q84M1shqTfHa-OF_YH*ov z^J5;%A0{X)JVX>p$d7L%0;d+Sjgxo-ppm=h0nO1$WbU&O;tl8iQIV|YOiG(cQs}?o zSki5Dlv{mgBYtw)9%$WY7Ry{RXU%rzA|XgmYpUUJy_t&B5aMm=?xIZ}Kyh(No>P$9 zSiljr>_~Ig+h=X_onb}SYINJR8KUQ(5}H8E=cJWw0Fq93 zJZA!ZkK&8?)~OhTq_bAx6Wy?ChfN{O@zT&$O=?6$syy$^%-;xb$=ZVdPhv3tPZEQJ zIa#A0lpL7tzaU1_4~VfoM<->AU@ZUv^Ui#$mykJ@6`qo@C%CB zxw=a3oaW%#e*Nk`+e#}-eKz-KQ+26!F{AilF-T}~ObFB|1_A^96SWZu3M)zndg1j< zj}44XkA(|!XF=Lme7}gr3TJ^iIQ(-Fd%yJw55Yj%039O*zI60KV{%aZy^HWYz2G}L zXa_qeMy8Oqb@ebG!-!`jP%#i~zO+DN6+qw=U$n8^2=*Z9Kha*}oN(5WufSOxv;PBeK#so$(1eg~ zfleU6i@*+S4uUwpxVS_v{w)?9_w!gx>9J36>LJDec4}!QjqyCkjG}sAb{&Mb~ zZ2xYqEd=Tg@%|681Vbz=|0=`6)qzbL0(Nu-$xHv+=0$}1M`i_b0r0T1v-1h?13-=d zkcYW7+h2bKTAmJ|zm*(+iC=2)^>%@gB3@4KKZUscdot+%y*3Ph|7K8uzRWWSK>ug_4cU3v&0oGa{-2lN zZHlT$|Iq{O!FHbiws@ImSC^OFP=>y22;_g8>Vp2Y z(8_-x3$UyG|JKU80AF@Q0%G+t=PUxO+`Ru1gPmo;9v}-fu#36%zeedly?CLp14BS+ zP-pO8*A9S%gPr|<>0UO^-1g;aaenF8-zw0{V*NL*48$C2@z(}&^6&zHPEJ5i)RzHx zK|BC&j+a%n0D1g9;Q%&P2-M}p1@Ka$FTj5i>V*1NUwL@|Y!ZKo{)Kn}Y?6NvAAn8j z58?-~N&i6t05+L_5Fa~$P4*As0Il|Sf( zPxT+f|H7yC2fgsA|3NQ&8h_9WpXMLL4PevygI;Ra{)1j}(fNa3a?$;RUUJdTG>6)~wDTXyO9qzUmrLUx!ApXcP* z^&lzX+jFkM=mm-H?_1Hgzj5|COP`x<^$6H)jeq%ML<#?K>;E za&9Shbs3m)K`g8Yj$1T_-7myv7zmjiPd8_?cjbg>TdJ?CtdEiCnhsdeVJClLqmhXC z`MJH|j)iP};i{1`!~Iss z5Hz9s$`jC0_m=;J%*2T7xyLS26&u+f1KdPXM23-3qSBNahJnom`__LvMt1PRlHpw_ zWzU$C)_c(G{E%l;y-^oB%dd}CSfDAxvW7MaM5+i644nQ!zb`>1%~MWuDXvgQJGMcI ze(-0r+O}5(zkk?hJomodD_tK1=`tA#Hn{7%`t@EBYcyMV#JS!aEi;UjtdE`Re78CT zN#t8@2EUf<&CXZ7F7JQjSKe0Qi^|kcBMNM@q&h_;d6n=D)m_8<9kf$8Gi7$r}ud@A|nTZvQI#jR;fk0YUH-9xzOAKj#^K_YS7o?cldWoYG7KTnBU<9PYD*S=4$h4|=xw_glCxWTn+@O|B}_G}hotD>cUjSrH{KznGu zf4y}=#re>AEDGbHdO$Y`J+~o7qh8Zepo3K%di8(h=mu`1vdoV8m(2UQzO3RIl6n>P z+HllBqt`5ZccwdCcVmQT;f_W2efl4WEv({F=Tg^}o1aj4DE$)-bTcPmyVsKa@IS4b zhb?8PKmNijih?%8SBe5PkYK2eMVNNJ?*^mfbr#EwzvX;F3-r=iXvc0j-suukY?I_d zYVLo=XM3i5LZdsoj(_`0^Mu3x(sc(kQb-aB>d|7gj3LJ<`#1n2!5>2~g3qS=#fDlI zN?4f-7jb?+5waxE5#F(1HqAkei%8S_x+YKIyW`%>RjSgSoTXD}#}QNFK3U-x(wTbP zQ}b%vo`*_6Fdqp$Z*Y)CL@5!jVjV_zBV&K%!kC=D4L#BKHQ6Zyx1lyI9|fa^_IlkJ z9-QPxkFR`<2J0U62%iMrw9@#h4`4g00%39k4qZRZRi7It$GG@1B!|yRzV&A-^#))$z?(Pfs81IpW~;<4g@G&KY)z!@C=l$bKrm=;XkJmPj=%(gzQW--m4XPG+k(mgb(es6@QW;-@X z-n=aQ4Chxz09z4D0j!fsv6(224UB(suOy)oZ)1TTb6_r2n$^*nU*D$8AGu3;B%qA&9v zUKxWW7-uEE`4K#s7iYV?k?rVM-P%u6L1$T}+O|@R#cFS<6p<886GghCu9knlU1214 z6y5_BsW=fW(YZf`;4*u&+{dkxhb9%>5!4@W);s-9o|i_MzJv{K$}wpBR$(JeQ@43~fP;;|sS7?nN& zN}4ocmXV);XOdLf-_f;XcW1^Q3JL0Ool5u5bQJUMhe| zCqVtpZ)0z&$}gI6XPSQ>^L}M6bx+KlW|}uQ9rvm?Z44mdS%4;U!?5OCb+UGEh3VTu z#C-%(3a5UhN6@UGI(5GWDeUTXDie>AOGAbP%%|t@syB&S+fnrU8BGsXzIC;vy}cQp z6hN<8w4Rx5rD-LlcB|{+81#Mk>d+wP|Y4`HU@JuFW!*3t8KFIkXAa-jg<^6JY6sN->6Sn|o z{)TYpUH-5IhPYJyTHh*OV1y%KZFoBl`Mx4a=t0-@+>#mc@q|@^H8t0=s)UqUi90SxPrKgzh6yI z4AdLA-PtRopnE9BM-jNh;+L|0?LK;~)?zW)Z^3R>$40usos4n0 za;?>AZzg|qbhIs&priMrDfDYAF10|j5KkoJL0}8HWOcCE5Kr*SNUyLbl*{s9Vn9`E zf*T>E1EUjRxAVg?$(Uw~%0m~X^$agiILC{~!xpwntoF?TGLogXY*2=^v7Agk4{eQuz%T!(F+L|_4K2GDdHUz=797Zl*1%%t=SqEUgdu1;aRPpej{2gHa2LF3{ z_N^Whi%Yq+Whd@=I}>SAfZlgvHk2~z{NKUWZjW0&B_;9c$t0|w^Oj%r?Y!x*(`N4cSL)2t2qCgils2cd|C9WLnP3NRfX!Se=?mmH43#F*%n8;~o98*H=qmMuF>j-}jse{Be(qL6Y(giW5I4 z8oE2jEe5l&_B9!${_yR{sf!Odcf)@o!4u=?aujVmE`3tAPQ+7f7Fq_fCfVA)Ic&UR zW%jE4@&Z-Ku$FAw}IG$`nB%X&DPcGn=!MehpWBut!90ST%iOnA&1&30)h=G>Y76i~Z@?C`m8V!U_`=IZR74A0fZFI;L${d1Z@ zuJ_KWi$QqQ2}o}z-g&VJ(F?BYd1Rro$)wQ{maqFYvY|sbry_3i`owgr$Uk-`-Ott<3LN^`2f=e1q&2zjpg(ec~U%S`!X-V=Wv{o7by z_639oGe_R|Rm3A!h)!B5fd`&pYK_Da!?Z<`HKP^t5Bu&>2Rw=#{52yc53tb}=3Dv{ zsx8FgCL__0WsI60${Wr6`n6t6t_1bBR4nqKM2qy0jh zY5aJ!*cHLj|9kL8A~HI`ZJZCq%Ux8P$*W26FqE7R)QFLDZQ>Z`Qr|IO046*B;`(X6 z1b7JCtAvsy+s}V^Yu)O`ZrHuv-D{9>$+!4$OM|TAJIsy~N9f4L+hnEcr{`V1gGPp1 zYj!z4Eo?*?Fc>_2V|D$coSfI-mmE-V$A$kmj7^;wZ`mbPJ*=FXDAIAiq{3df=tML6 zTkG`kY5(GNDK}j>hxAtSTuvOo^CUfbNWdbMy*U!hC{a}`g0*V$D}DY4R4+GLWnLG8KuL( z8kv9YT25WkcnQJ&dMb8B(Ez&?>hH+^iBF5>>eP0DQ zCq)JCayz7YHns4yqeXB@ZYsH4a+@*4iOr`Vwyj-}<@n=>Y9^x~a}mrRhhGhwZ0UDq znPJ#TkH+W;NKq6rQfyt8^J1#MPf?y6_MCsxo=ltyXNPm({1zsr?K5r2#h#4RL(co& z=Vn~HGLGQ&Rxsj`cAK87tB*wu7gcX^iJ;99S9C!XaiyJ!5aozw%s84-eFn-}nBdm* zC3yr7cglA#@o>3}1h+pyRQ&W^3~wh;4Yn=RvNf>lYN1KucH*A7i?&hu9Z8B>j)pbaPa$2p8o z_t?xmIGc#BBL~f{G+0h66V^i`d`o3SX_J(HYMuLg4NH=nV9DCLM`7*rqld{lpFzYdWz56{@OEizo&7Me5+YZYb&(FzB5g30Ys-`$V z&zv%hqlr-Q?pbJBn}MQGhuv7O*RyOoT*YbViViGqub_q1xRXhkTeeFm?`$Npk-!RG zETP$<5vTF1i$rh4-cR!g4L*oFY}iKhRQun5JhR9fmmubM5_*WJVYu5}EyS$&pgfl- z=Yw_|k=>J(Lhr-TrMVpXhY5c^5TQ{zfm5@EeZ2Oxw4;8$JLku?e6M>Vvi{9g*#vE$KG}YJZ}00uOJHYkIZM+|z%U_Z0*k=T-YJ?^v1pGDIrL1+#xf>a=X9eSYZLiC-S& zU-iL@wPabgv?5G9q!P)zqmW*LyLml~(3%|8Qkg1zjd-4G#_7YvDt540f~px?FRjkA zMpwp2->c@$#PMSp5=EA!!4mabVyRW6qzx4NukS1346D&lzl{xc;AArVDwQ7mjBI2h zZA207##4n)Wzm02%B)=^EaJbw5n(=8Ax*s7QOEF*rb^g>L*Y72#Wv7Db6Ud**_7{+ z*WZ9s`pS>r3!{Hgo#F;OS`1kFoVc3uqb2Y`itn-HEm49!%tHh*r8lwh;Ktq5d2WCZ zpvqKXCYgQ_N|{*Q@cqz1Kp&WZ=7^=j;WRvj)!EvOXQF>l>pgeIso}u8*57U3K>1*Q zP}QioH2r95eFV?2M{E+f8C8XYAZ^f49lAiW~R_bGIz`Zkc})r$uF-Eu@|@`Izc+Q+-F>o|eid z6UnALbd3hIPe&!R`}sK|KJKuK>`4UH?c>bZVq+t35C)3_-N$B0jJz=}7_u#sKcbeKXaRuwCU1yUYP~(y9`t6V=vI8*`)Q%K< z@$r9Hg4YHew~wMm;$R!E7XuQlVK&&P}j^+nj#|^$Hg!_ zP6o!+Z%~!1MDU1*#<`D*-p-Y>F_ikH>Zf4s4_Xz*vbU@s`U;c~i&2!F-aAGMKHDY+ z>(WmkAcPri-1rj0r@aG^lCbvszb7|^e*J%a7MDOXw&fe%5y(g0xc0Wb*Rr8RV#4iJ zJHP4GK=WJ0$H{_{lM(+#DF|7-Is>9Z@v~*Gso2q`N{Cq|j_^1dmSyymi?g>X?PZ<+ zghn3b_>;v9U!Fmg+DLvyk)vUY9PR6!lHV`^?5|P#$H*<;zT+sYR*R^@X~9JjOL2cF z8DUHMrCR0H5oc(;(E~eLs+0Db<=Lxb&nIaU{m8K75FO=YZ;E%TtffD-fGvzNl2l0i zOy}l!GsO5CJkc3H7E!~1sF*+ohsYY@{fwB__x=2?B(rEdkunn6DA#N)1n@&>&osrM zWa>{#X2r$SCZnU&N$NXiqCS80xK)2Db0}_??)s?xVE478Ive8x1d{C%{i&lN!y*ki zMSXwxcI~KiPlrDzB8z z6*ElL=_d;=FVv55A|`zkGh}OB-}qT@kYgv-0(|lSKiyqVE1ZVJNlmIQrxW$q1xpkX zl@xt3e;XR-ahcani(EIH%x;y5n;q|=r-mLh!QNrAqTcnmlickX;rVq{5BV`3T{^Cx zj|>vfPU#I&gN5MC3cr8qh(LeVKaf{6!IfdVh}euJ41G59U!tm=U`BSC&Tae+pn+WK z%dol`@x)P)no+Cgh0|k8e3ohvHa6Uo>c~z$M|g6b|D||E@^kuTzz9_t`a@*HoSET? zFKIH=R+JEX_Qqw|&FwNdh)`;MekRuK6-%#%)~dx*f&mg+)Qse98nS=%vIyo3Sai)MK zIsYv#^THJ3+Jb@2eLR16C1vP``oJXi)9W&Zp6bc*%y-`ERlLTw8+@69Q>j0U#UlR# zmBLsvc4AkWWx!`Y(+qD3-u@<=4#UVcN~|xg^(-xJ9y(WJmI|usvuhQNMOXA%=>??MV(0eLmao%xrh5_ ztDD2Y-y-L5Z6-2#_6i%nR!@Wq{PG>-QZNT?W%Sb|wgD%$icT3qW|3fxUhB;F0AzM5 zm9U(+WzWFHb~e$E=Wi$c>t^4tRNr%5X%-)x6hX8R>FU^S{JbS99}skwlEQj0=l-L|R8xDTrlwqjco+5uf@-KPf)(clcLGy(B<$KkXOw?|EXZoGLQ0ce9-&fv`y+es z=F`0&7{+rF9A%KxdXeaAua((Ts)^gu-#d={KsI{y18=6))FNf|gHE{M0?~EySXhxz z%GPoLUbcsX8}K7|_S6~qaAE7r!24~@8Q#LXI8e0hV~i+>RVnGMuwN*;$czrKS|>5m zw<5KQ4aI*ZNrIu;yY2GgNn369u$J>{SR{u?(jHHD7GXoG{)$}hf~ean;x57t0URxc z3~bWyyg+B=F}J#_z&Y!ACu5lkBtM`1u=@gr)9OuXMrO9A-r!eM@*+F3@`)vIR$Lww zTX@lPb(&ARQ4=1Lai}aVVa`vd{jk)%x^+A6{-b|HsC^<@V_EH*aJF|}*-iPD00V@; zxdt);JNY5^(HaH(P}zuW?)=8_t4iAD_Iyt1`8;TL7^PMj9`NASRIoQvtbkFRbN*zfo!BFX)RSRm))#~P`D+PK zIud{8*BjdcA#^h(3RQV$xsnwj0OFco^#wVq$P+*KKIs9~+T99UQmRyW@`KBxS0C9c z;hj-N=PM1SRf6{s=fl0R$a-DdIMU(+C(eCR+h0AEaOfJSL(}{E?wUjR;Bz?iQHYHm z>Ot?y%%W}Qh3sV=6SO>OahC8+g^Jh=Z0mo`Nu9;ZGB_j2%kI=5KaHPmRU7^9GC9q$ zr2zsn(d*Vl?#hit24o)y(i+=7CC8Y%1gm|lyO4tK23mx4jByi7`e?OzLS!Tu1mGFZ ztcNOwQ@0SzIl|>;Tl7;+3I;R7ws8tat$2IcVMpdOy+rbN?pzptA>4J$QbmlwPpyBA z=7pUYI-HTarb`GGT)3(-1>0CM<;c8Gh3j)R=7z~QPGgEVh+YRl5{M&C{Cb3{C9uDX0mbpui5CQN+@OtL*!mwfjHeM>>J5}fno+} za`07ba`Q>YE-Z=6mDw%M4E`7~LB6^o^B0r2EmhqlIFfM4h~#k)PpE=OB_>|W$2zUA z*{uxW4~EZ!Zy*mJCTB#u^KRDgNJ~6B#kRa#U1L9xvp*nPS%A=g0ik(VopOJ<_J@Wk zuLn_hCcZ^PG7@0+UWj~WbT8K^bL1Oq+jMWQ<{hTMdAk_*gLHrrxi)@SqMcl~iAN;d zcr|o`5-v^&II&$*lq}ZA`Zz8{PCtuHxd!_>TcCJijV)-6GU}ENjI+_ihrox`AIIJI zIh24U-~WfauhC$$43*kEoZo*%y!>2i$%DW5JDV=Rd)9A^7=H6yza0JM7>9; znKI8~HAF#m-dAgy!hO_+-cdy-L>FBH`p2G%^D)Yqt1&1JdY-Y|F^v0b_8huL-b7uu z&>fbrtQ9y6IH-BW+4oEl=WT3X1*Nk0gVJ)mhsDv|b=Y&OnST zm|8HGwE+lUNZkzWoNdfY;S6AIHBU~sMA7vKPbn^canZPxiyB+>B#G`-7XF9gc3575VD!P1b*w+KM^RfY*uaz{)j-dJ7oJ(~FflF^X zuv}PG85sJ~6A^X$u7mFsH4iioO|VZ^?6P5AKlD&0H`oV?`w_;OzS3L9`H4uG^kYV4 z1Q(qh7a?^kc!sjVX-D^nk+0hID)D!@na+Nb=}ypcEotCBmDYc#vr^RUoebq&wM%*t zi;_Lk$ZD>fAM&Eg2<>W&iKI?elX|b=3Z)-~SzBIBlR5wCe(zA0Dtx&9Rtp*&HB&EU) z=U2|jC-KGTFyDXjG;iGhyn~YBq!LO=klAHoXvbe<(#VMFx>kH^r1zrzzJ)*WzE4Yz zg61m;+xqruHn?SP>}v+jJEMZ9;1TR3L~&n-$B*sq?0lgR*>B6Y3Mn@ul1u?>j$qCu zmEm}b8!s(ejU1?667mP#(Iz^@OC##gqfLdk2~#_pk4JwuUa@)4A3NFcvRX~!d{GLK zGgfhu%q@#oxFT<2v&d4F_i&Ba++MM!rV#o-x8k)K-gyzn)$~ww2|MxRKLQDxwy;o? z@HeoDiY?|QR-zTs(syA%_98NC(PcuY_L?f|FrJ&t-^1?xc}S6YA4jLW0{HwtGI3C( z!Pb}Vz@&dJG0XfKO28KGk!3!S+$XJsc~{goShn@;naG-=K~Q-tz!HgO@D-M%1Sj^% z79B;jXr+=0;cYWAZZBxUbC74L=5ec|z4ElSYg>h(yaPVeq=#0e*v{@Dj*(i=+Dvo4 zJJegJQ1a9$IPeJWw}ebQB;jaejp@*R^H~RZK)8Q)so@xyvUF4Z%U`2Tf{$NGGM1SO zI5p$;2G=@u@oeS&2+HT}-B%|gYwEgJv0jYkr9T%+o19}-^LdlcQ8W-I&5iaQUipg3 z+SHIe=jJw_A|G}{h?HtF%9 z!gbkiob_(R?=Rba{)q537G4 zxBZcVeoD(%s1^FE4axqP=};<7)at$?>9e>Q#V^7^`Xhv?%?s|sM=P~2?f^w9!^ zT+`K7+>O2!%|n!uK{kYm z#s@WOmLeKo+lcXgk4RRtZGI#jpg@0*BqZiA;xpfViWKB(HO<+=y_J4r9e4r%rPx=g z(+BOYX!Kl4z1U~~{qYb3>-Mo_n-R*jA4n1XJRN3Tr|R&uQjixJ z1`Q+A+DUlJLt9WNXOn&1&mJDfN06EP`8D_VGV$b;&LFa+Y$Y@Su`i~Q9b+2YQk!H({RL;YLx*qC-Y2pe}O>YKD z)vI0(|7*Go-Gmn0DH)M;$Xh^mG>QK1{SXw(Or;YAqIsZ@22xL4jBCj)k*hv%cVLkB zI^fSNMR}4){j?o~-O<+fp=DomKPgkhmre+Bf{RH7_~qim4DnmitIdC6W!ynW$rjn^ zU!Li3vF6pf7}sts;3NAz@_t?HH}l~CoM9~?_&4Gmkhf-p!V%%j#+ z))q}XawIZ7mO;W?s^Nb(wv#s{ui8q&kr-t1Ae;Wtm18S5G?d;GRMv_MTN}*z?De^+ zzBY&$8+z$f&3k<^B6#wMdWk6CIQ^T-eGm=h-+YOT z3qWaM$S;~&Yn2Ue<}}eJXoq_E81i6cZ}pZ}rY@ec!pRD-yRu+XPxq8($KR0BU{>^r z6?ZJHH3ssRy4Qb3il4A-k{F)Lg?QE&L-CmbbyYH_cy;lEY^0iP=z%&5n3B(kwG#{wkOhiD6rN4q2M-+Kf* z4`Of0MhCReT^JfM!5d6@>|3%*p|j|Ji4E|cDu6!OeC~flldnAbq(L=9yL2SqWI9eE zY0{{dsnehLV)jxxIKL zCmw(M0qUSpCQ1LYnNgD+nM7BEN%o_^+e29qEA3f+Sg}zX;`oZIw~}g=Txp~xPT4)E zYNC1vA*BMliEgxzpI3I+@bmL;*?)I8RW?*zqePMURwYmC1VoVHvU^3{=2}I&)i|1W zO;SR^5WK8|6oy?FsoxGU*m0Oy-I7IRrvlxLszl`ueKVIAMUKP@M1U_ zfz0{$?skdt>at85L##b!PaV-MqL_3c6r%7GjK^;eAG;AcFlw{Bxkzx(`MIf40IB9xnNN`(glx(}XUulm8!6MbH0mF1kH!lRkShF0j z2Q2Y3?n2C-^W5L?_zGkZY{lv;`eDeMsRu8Ov2vLw_M>#>b0hdHC@izYVQ0LjeFBxO z1Szcp6S10edCwcz9h-rs2Lk&A@|SO1e4&&gj6KFPAPs=N=$?B=OKRrs?M?Q zXJ*zbj&Z2u7YfUi8j{ zrR^~8tvnCE%~G2wL%F0zX5)V)?ZAlmY1nf{?1a3bQv+_+ZO{fVd{AUoi1lb8;N;W% z;;hwUCfhVtYqUUNOwFtZ%BL0EN7Zp`K3s>k;y@5c$$G0;b!~r2kJd5=!c-<# zkq`K`TV=D7ff~JN!%ucAGMgD6t7nr?ewn8zd=@2*gShp~xhd^%7KdMS$^zl-vRlf) zm5X&?JYy)qJbN$B0j>U2un>G%g0jcl_; z7gjtC0pUI3!`EoPHoS4{qJQeO|H$6Cu5ly*KkMB1a|XBiEHy(Tp-6mwejm_fvF|>NaGHCnewT7?+4_{ee}(tua>MId=+0BvoHqK#}k%?yeqNU?#Dk(*CZ(z9U{8=FA{8dC0$O7n$A>8rb( zYuTi&HBpk`@_?WQ&mzA{2i#w)@piy zZnMJm(Y1d-xIIPD5F+B+va$r5E7`4DI1Dw7!xehdZwGjs7+awUj-<#sk-dZPE);n` zU15MFZ~B)A@)<{TLROl^&+c37Dbu$jH(0oznlg|SK8Mg3DR#gdUPu?y^fs}geNhBUL1zZhv*>?Jzb0O!sy_T^Opp5mcY zfODvD(dfzZrD_pR=#+mCW&)wt1{TT^AP81!$Mt=^&r(mqtj(eR=9EC$@vEL?4Uptk z`s9bOnlGAh_$SV1eXOWhfOUG%fk!)F#(hraeb83Es3mY3oEL<3HU7F-{zf2)4o`3; ziJ*Vs!n3Y%Y>H07m?x;Divlz0No-VJRBU(5m0hv1h4fg~?P{U*vDf&a*yk+n=4pLl z+?>yU(DnMu+n%2#!;i9X6C(A-T?RQ=4o~lF)dwv(tP)umwXxj$iAt4E7|U4wu?cT^ zCWi=!?0G7P5hiLS4E+TTRsh*%hb*!BcdsUSdiw7@Yhh<|^wU#k3-MoVX+10==zH@- z%5P@K#NdkmAA@n$UY9{N0}~lDH6Sn`Z(?d7JUj|7Ol59obZ9XkF*YzXmj(L+Bmy}z zlQ9e^e~owrRMY?0KP4d2NZ06&fpm9wNJx$WgRvou7U@PBqy#}a1f&~D>5@i}Mi8V+ z&}aIs-}m?bpYuFt=WL(*y04q>d*9m`3xlo!kDMLc7OV`1A$Wki{NexwEdx^^KY*WK zke8od0EdOe5Q2b$|H5%tjKOa15I9WyKNt#be_$H~5>vE6Acawx#uH)((BcKC!(m`|92Nz*tB)JR!4ZLs^VcJQ9mD|uiiwGE z|27B6xq#guAR8D!%Ld^Hc0oo2*+2mXa1aEH@cEYuc1cGB!d0A)&&$h;*T%)27w+aD zf6c)S@PZ&50s3Hfu$w2?4)CjFfVPbb_|MXKaaaI`ju7`h6b5j6gqMvQ7=Q$z5D*yV zjx_Or*@4{v$mjqAHBEqyD;V|%S@RD9H{j3a0D!!}zuo<5{mT&q_S@J71cJM`+Q57u zFb9A=1PTV|C~NW}yb;_08<^cMq7Bp?e~z@b@w9KUGl;!jQHpmM9tj`_f z2608W^SVQzziQ!b{?*LMli@z53rizpByBF^AF|#MgWBP#e{{0fB^7Q0N5Mk$oDINp^q#0f4A~C zjI6=m&lTBGf5I=s4??~H|IbVC+vNW@`@g*WujBvU87X@}p}+O) ze-Qp3y^RY5>hmW9InN#ls=~a?W{x zk9heX{eeT=l_B0>J6#9@egL2HFX*=ff8-Yi@Y&k9{R0RgWn>GHTk#i!%!CgN1^-&izcB$IfY0IA zwFq_tKlMPK<=+Y<8H&vRzfjRXP!|tmUVni|rV9k-;rDl@;pT=^{+?!J2mg|PUs_WI}voH$>NrXDR z3vIiqmZSFK*&4-MUF(QSFzt&JLa@%g_H)$4%2b(pDsOF9NiqCVcfK@ya`qWD=i$|u z_mlV8K`Uvhxd;@cz76>!p1i^9)AtTwIy*R(!??)Hs12fTiF&jI*5(-IY{`#jZ^J~o z1$W+V7u63)7UZY{F%eLBCtHE}@DquxPVA?Sh5^N|LRe_FWEtW-|-$_b6tW|rYK zA09*>2N^f3~6Sh$A@jAuZ`tP=ZN&=6>&9VxyYS#YZAzP9!c10deu6RHSdbvkkZ8_Oa|b0|`@X4om|WmAAwG%oVJYp~#R}n(*HXbv zQmLYW{Lhyrj!+f#zQz;QY&_8GzP@LfVC#0oCxC6Kf05f^=4f54&8CiclmKbiPq^Kc zH=PPP5}94emH%NBU`0~${7J6;i|=vgJ7@LZa!urFBh8>uDVdXxq*D<7m>m zCcgHxv!(8MtJdFKU>uRy4ylL_FoqUF%Nyc_S`@C99>7X~Yfe)rTBT>i{w zq9gplM-JXubNGCJ$Kvr*YQ1p*dl~PWM4l*;@Mt^tJw% ze^4135a&uH<6)&`fPTfcMVU+ZCgDd&ai~U_&fruWgW7}Gchm2_?@*lU6aBD4Oh1;$ z=o~ptAMlW6XDUN*juewjKQMZo?e}cs{9Mo9MsQ2H1d5*8aVqMKQ9+GWFoj4`eJoef zUAZn%%dlv?GVudXp=Ib{h%vnO<&tMhf6mt=neN&YdWn?m8mZxBY3|VejpFi-CyeFS z4XK&rlllkuJoOb)=L<2i^%Na(DDkIdt+;ar$cD2C+MS8XUf9}0cjVNrbS=@!YBRhR zGD(W;)CN_3o!LQ`f+61D6X>D`gT5LJ8R@Fk+WTpYm5#boh2b8KnpU;t9q@hie|#T5 zZl2fteV(pKlSL;_c0WTkBs`f|az&RdPW{dBQh;aT40k_s9iyXBv6jqP?Y_)fvMc9o zsE8;29J$nzF3{b7Lxmt&wm>^c$nr=qHOg?SR@A;F96;rxR}>M_tiusnhgZQbGQB;; zf**E2R0X%-CQ0A-4aw=L{TIK8e+f$>MaShc$<2JfjEr^p)Zx8Z|C_GMFe;I!Y$24}s2Y*CkDPU+$Pa#$%g!1Bl1i4L+@EyeN--{|IsMwH96rmn9zy8UE^ z-OtQpy||BNhd_I?cA}dAH~>+~I9hIm9}VPgp^L)i9)S|$vy)+GoOUxaZ*`nQQm{q! zoZWYk7TX=n<%`eR(ilp6HI%X*LPG#aIc|qW@Ty6D+M!l5sp6E~xH#Xr z&niWC9)2K=B(KzzzjaK`Qz6LO0HNSlCIV@o9*n&gXQUG{TYIelXhvE7KnQ~aboVB! z)j#kt{aE$NYC9%6w^kWG8$QTKjJ&hZ7T)di33VlBG^jZhoxv}mK{13*g+L}8vw%S{ zYS;63Hr|?vUs~TIS9OPOs_tWtm9>8O*=ws0p7&ia9Z8pz66@U&tmuP=g;0W+d*`ZB zUOi`764l(r?zv^4f0IFt0FAynewE*bXibN~&%|_+-@3Ws{3avfw@R?+m=S0|_GBp@ zx(Wi?Bg#Tt-L|a7#mGa6KJ@rqLdWfAla*nXO{!O&#sl&yw#6_bUNn=(Ptp=*^(RIh}Z^CY_2p67MkP zQCmG+&D^LP%qCor9}dhqsQ0Ds!neb@8VWQayW^+_e>nc`-XkP-kN@SeH5koG}JS9G+OKeb!mFXr|j&jDA`9#QQ2kN2cDHKtOIK)5VEHbWjlW zKbsUB>9Vj3U%bcWwciq%;{Hd?>X>wO5@QMMG)x0xRvoztLZRu*fnKN4`cTb=HOfMa zM2<>01#$xN?_4`e2@!OM-nFh1EGov7{PV)0X055D*?u233rH{kY=wrs^1vN ziz`v+!jQt`YxKEiAw*`uFhyp)2Ht=ais!!0hPn9K;r1t+U!2i}Ik~>ip56z6Pbk*x zp%#6&*YwP$@vdSvfdl7;}m77A91H(Ev^j8zWmp?Q0jl{V{47m!|a(puDj0pTB;m<^Tt&i`_NtY9IBvN)?<^y zO`A7s06O5S4K9)St~ z^Hvh!2(f3z>erV1Sdw_}G?n(sV^qy@c9Be6CbGMSw2IY2tCUYjgut#QwxS0HePbdd z+bF_vtP!WXNYJD08!7)maqU1hETHnPOUAs_4g9`=ZekC$qm>ZEg_X@Pt$aRx+fnG& zq7#$3octX#xJ7Bm$PMelP9Utu8rceXri3I62YWL9BxrXKw!-LoQsW-|x^+8EUz}N$ z+rLV;C(X8K7fkE8Og9-Ba&i33)E&#>Y1BiiaSGU6+Po^x;Z25zL8V22`u#I|<}a16 zY-I(&t0<1s`xc_u7M$T#=b0ufjF_L>wTu=$Jn`Q@$YIx_g7d)@L?6WMELTTBkXJ3% ze`QQ#rK%M2qmgetF%u4j!C-~_W1d#g!sjvT0)FLcs<)M7E^v6bUk9ASJaZ4_^A z{Dx?sszeRHDcn*Uf*C9JkVA(+MDDh?4lrz2S^M%C_+G`{`=%1|V!`BUxx39%3mDve zKgW`W(QXfJkPhaeFqB;7%}wesBoDm3p`6sbrgtCN4CJ~AnJ2eteCM#-)!+wtB{d|rE*c5Q-%PtMN&_0gkgc1P{#rY#T-3{;= zDoC5!x}vVwma;E=Jh6_NI&L(&>zKMRZG|baz#zS|NRqrh65y@WY8|X9hwd4Y%3SIl z!3MU}jNn3qY=iMXtPrE@y_6=$>-7CoEyKQv38eNP6+zZ}x!&+*dbA|(`~ni5p8T*u6Y3FSrcQ9!yim5i}FQZ-HueI=-6dVw)KOb%F+n@m>lsMGk3|_ zN&+xR^WAXbb}b>hbKTe8GWIs(OjE5S<8Am0tURZBd*mLFhOymZ+E5TVQswq8|Ay$j zs<{odF(CGuRjK1HH@Y5GWylEW&LeK%bn^5jNcPOAI1M%&i^PDRhR=NL{QT^?0_(M8 zLp)$>+y`4~sLgan0t$(OxI!OH&!>CE^nz-&r>=jt-J~!=FLYpObI)k4KTd4$2KGw{ z#}f;1G=$Cf!xQ+7-zdC@>-`QpEC*leUuFKAb~!YHYRj@s6PJ)09y@((*db_eOxP0S zgyoP3$tIyFw=e;+6`$+OWFYluc%4E=E@m<+kyKWe?`nP0R=F#xw6Q+M%V2-wuHPU3 zd3n~}((KBdId|WmXrnXVH0h?T_G6cpW8youob8S2!hII{MpYn=Z(&Es6r2 zA7X^7j`z8ZFoggIUz*ZGCy%T?oZ3$S3)#VKbMgeWrMCglRt6wvUpgg6d^Z-!MU5{T z!xzCZnZSmn+mcrS=2Di3*ekx*j&r@AH!vf>(kJQWNu}o^cx+#M|R%NH% zcq2H27})?|IGy7afLLExGQC)p`0Lj&nz!PleZ_Dh3Vf5Fw;zqMpm=)&BX>YGox#)N zLq<7WH6u%#UzfUTZ|xZUPm!H@7q&#ST>K->2B)zR4)viBbW=Lq6?BH&vD7JKS52*? zVx6E4=+^^s7G^A_TQ!bDS3;Xyo0W8K<1$3;3rQ$|&WIQ{;iX1yZD*&AnKwHH%^N@1 z`UB>rx(Xso-NL3r_ifPjq81=dB*C#^{rlqw$y|H0e}ixL6z!(CzQnaU$gaUw#L>FJ zk+%QvylIbZ^$DhEdfPL0u?{uX%3drjB@vqEJasT?$qu`AYAZIKVy=sD#cD7`j;6c3 zyciXb+E+({*87$>mwitE8WKNrxDiKD{v+|282K_G__VzMd<(1zWnM@{eK!kVv>EQ0 zaE9?_9o2Wy$)3|wsX4T=yI^rcb&=X4^{y?5BSX~kjnuP!tcnZ<0(3njEV4E@bf9o( zVGP>^Et-fR%Fvq9J_S)_p#h;e8lt3K@ZynBaf*FAAh&>Geu(c`@LDJ)tv@bTCcIRNU$n)t7v5*?ov>|| z2TiC2we;>=?jNY_hsemA?J2C5%T*Q6@t{_)B)+zvzo0`R$+YuLEU2d1=h0ch8s4nO z`S;ki!joPg2(#yK>R`HR^-^g3)ue@45CXOoQ+ZZBxhYx)iYx)RwPs^jy`CX;u($RWYhq7gO|WCnu^?~ zK?K-bPqC#*iw@Q`=K`}TIle*Wd*`SLZ9Cb&^=Xa18GseJ|F6A@mR&g%)H$Wk2FjUo zhR(7hTvNxb$2H@A28GMsJl>N=^F~u9oh({bE5;E4z5fQ&T^^*_&}BhVtmn}&F5#}~ zEi5k8FSnsXDtHsd@gxBJOw_w4DP)4*#2lHhib}!xt-osJ`0%a;xX5Ehq={8ajclsA z2@>em^d9H8z0_^M8PQ`+y5~`KmbMTcPU|zmRrX@dXSGnHGH}crH#0ijJYL14dWH?k zbM^a^l}|_F1TAB@C$xt?jHHY`ge}QmyQd#+x;bx9-0E#q+6{pP>xc}NHnEK1Y%soD z%?J9_Fs*|LaVX;Som1b^Oto2o31N9#vO9&R_OG_Wa4xwvE6Ot>`fcaoS^46hCv(>p zh-k|=5WcNBJK8nJ*p&!V$RZ7CSUZ7zmUB^b%+Y08230Ahp|29eqyk+QgM#PxXk!AC zK2=2B?2V>!Ox}R$MAMxC^`;4t&cR2z{OZ|H@zwooFyqn!sB9#xgEyEr1~+JJX81R< ze^I;kcU}HRZ}lPvXUr}s5Ke(Pm`~8lu73ndD=hIKlms0eeH@w*GBxZ&U$BJ)8_n8B z#W-hfA8-{jjE6zM=~J>Sf(_PBCVZRBuiz6OWaHY8?zDjRI~pg|SE_pJ5ox!MD~pD? zb;QT39ts|UsmP2RSbf}$rxk{jl7 z$gMx`cnG|t#!0#;p0j%{Aw3m0@F&t+XfX)baRvOM`EI6`_!_vuf?p}OTp_lJ-Ja@C z$RA$oW;%g=LMpVCjZ1_}9p^tdN8@?g&C=N2Xj7ag)C-?pk)70yNU%DJJ zr?ypFMYhTKJ+mNuwJLSmRmjI*!R3#3#P^^Nf<^iXbCs;lU&H1Ha z6Sg~dIQ}SU3>)eK<-$rMOPD7MOZ+9U532%=hyyU8y3ZFgiV@)NKeRVUyVd7;IjkfMzEL6$0NDGyxqkl!>9qsi6C4R|N zPR##$DJoVh#_{jvE)h8qi%!=#VzGVvHHNT-8@ zBo0Nlhw`H)pCdFd_nq;K?hsf~*P}Kx8*Ds?S|ECcm7Fm*@A;LSIzMO?U$5pjOz$1K z(V+D{?Ce+Xt3IggA9hjJSf*(Asq$BaiINLg=_t%&G_^g{Uw0%^dn*A`Xwgr`sm}p~ z&UFoNs8tQQ>ueOcM$wHa+CH0p`|->yVLfDsk=uJ5st-JB*$sZoJvP!6 zau>>O@N_#!v@DFtWC541>|5JqNt6vKli9z+?eeggT7C}Yv-0>NY{2&YhVUpjr1 zJi=N`OVV*MwY27+@Xqiq+`0Oz$5{XnlS9gd*My9?_-QBbHfM`)OLB#R*{?y@C48iD zc8n=69cGCv6R#j9Kf>?<%yz==`ZK7o4R&2IaK1366zkv5X5|r4#~c+^)E`rKtfgvQ zNUhhgKT{GT>S25`YVGENZ}pQs^O;qykp>2X%7q$_3wKfAJ5suD-NDh+!}EdLb4+t4 zDjgn7uys0erUHlLQKIX%I?pIYyN+~Zk!rQ2eT@VzE}miU6&u}w;={1*PIXFiiwZz; zMD;x&UqZj?T(YxON#awNp1lW&ZyUNW4n}bhY4sHZB?0O1AqmyZTFxhr$|G28LZlP@ zoLtFUS|sbLhH!P<`J9`3y$6VJhHE4ErVWjxEiFk8f(Sh|Qi5PaMho7mrN#K`=yl;K zbgC$0vnGPWhav2@RSNf|ED4+1&n{HmK_PC>!mb-+DA|paJ`Gc+?$HdEA zVAf!)+n@dJN0crj3S!)9D;kp<5p@^&5EvHa9{n5km}@xdtnWp+*nF2bp!ojzpNWx5 z?@|%gV~LTg#cUSl*8)*T$vL?SdvaU?D?JE3RgX13DvWjTG$1gSj#LD;%t+cBghtxy^Eb zZrYgmZiC429da*Jgcm` zzy||v`vcoKR*r{oF29hLX8m@JS@Uvs+Rk#J{XU5Qmj*Hd1DO7%<-nL2$?+0H zuohd(euKleF`+x$Yp#e+u`{Eqm&5qXxzh7L`IfmJmY zHe9RhPhG*P#8D^273O6H_NSN@OrN}b0$`)W-_3!q^G{1-G?osvanC?D+WE07 zPJ@r?ipr@b%MJ4yVnr%KXO+@~xA|k^??Zf*FsSn|F*Sf1XbJAKWKAc`W z!;sQ3hxTlt>h8HfR$s5@0JaQv%HiCDX zzK^_5Yfs;TMH~)So}wxvEF(`GjK039F-ovFNV^d<9cNcq?Nbri}p$Lu~rP_`08g|G3DeOEW|^wh#kl>3N(v|xIr@dZh!|I zh-kGRswff@%TJ+EL;&SIaQ_pM9P`4wNGD2??-U4nfOMK^%5Wqy#N;q&34bycS7QI25-%WXAs)N>3M@!i&tGEH z0g#<9hPxTicg9cd3fRE+2S>dx4Yfd>0g!%xg9Q1?fzTdU3p}WeH->yaLONwBO6>zl z0f1_U{-GJO2%leH0CMkD(pTmcp#B1AfAt9ex4qQ2>z;Pt{{96ML_ms^1E9ufxJiO!`Gh`boiUST40 zgIM03nSc-UP1lUZyc3H2As|)KOa(yf?S(*4A_**Kg;BnkA#Cs7UR@kP+Ezd5Wu>Hy zM(Kya*5LPleISy5K)VEr$4P&|zaVR;)coRcItDtB%U`Y@uJEB+j7T6J@gM@nl&JqV zGsXLV{r=n>9I2uF5Y(W&{{um9{Dz?GqcKHw=+3h6K)8qfy(H3u!ueU($Jq)2L$oQ_ z`knge=%CW_|BIBW==QYn@m1k%Q?bpf__W~G{x+*rX3g?jn=zj0vI=bUR4)mOqxiunZJU<~sNnQ#51^?Jog8|w35UMhYp4{f@^*bva~+hy9TTsPR-%|uycA-zNfd- zis;B88olc5?mV=m@qd5RObkpU9m3W>KL)1GIAsGEDG1Il0*u1p$BAEl1ZD*s);?X1 zy}CQw^&wpvT>+%BD69Rqv1|-_gxXELB-EODS^GO#nyBNzPYn?m|IFy<=-41+5En2I z!O3dvFPOZO69DF2b?h_jxe3b9K^j`x|G)-U#4wKX`T==pV`vc(#-FDH%(v^S{MjM6 ze;TO{BwZZDT!bD4=RkPda7bjL=i25gTa*aIw|V8p6uR!?=Iv?h+O?m7p!XX)p8vIG zvW)x(Z%@W63P^o-C~j>Hg6_>sPsHz??3;r6CggwH2(P<|e7-hjdK~^5E`8Ceb3%S( zZwU*G2QU(XyaSjwUfy4HgBMq0d0*akI3QnyqHDXZIRPNAUr{}v_}rN-p0J-B zFJGA-!kzDiBVPb}f?HF=o2ud$_U>09cN6rQ=jW71wuR>CnLQ0}r#;LwP|nxegQWv& z3F_1Yd>K{~4Bop+5B6%*I!{kd5A_c}q!R3tA>M-Mlm(I;E(25ZJsCH614s~16@0D! zpC0Z8)vK$Y(ogMPht0JPzb9-aKGi@CxzBq3$plOhGJI-@BH0u9mp3$&Q;(lPbduTo zbHAO{=zs`*5;%g;!>@wgT7!91&tV&#{EfN=q)lI1L8>x<@XzmY6^O>d7l{x^mB>^4ipkh(EpG|_73SVu#c;$uexQ) zgyM2ic$l|!_HBloIo`#sIDGSQ5sWhUl8NW85Nn9f0+8CW+aqvH>hx(?qBLsYT)+Mi zjYgtX;Skcov0;3hK8V^a?4%6196XoL&fW9{quUxw{#q{*I`(_<3*ALbTv&^3ivq_b zT`w8WggaA}XdgCYuw?l+ryL&a!kX873Q8ICDO1oO=S!tQ!Of&u6wpS%<5@ znvY*7)8vUlo$~-waTsiwH?k)*Vx03)(4%QVF|I|`=}uJ4PgXgMF(n^AKZl|5 zPbj^uD2-*tx|Vek+iCmH!**skSGkLd!y(-Qg%}d$s0HN=q&FP!llN}1Q=AXY{25J; z8UkJ6_(AIq{yrhcHuXi9`hYT6uF`ywjR~!3)17REc6~I9cK>6A;_A8`HOIbZ0dej> z&*+%-KihmQ^rwUEvPy)M58Bn^)JdHjx3(9QO+0htWlg>mNM(1owOImYHITEErcLa@`fzHZudy=qKLB5jxYZ=ViLS;_z0>pTLx*z^Wrv^;nuY>AJ{N01L)w!(K# ze4HYLR6@KBz%SymiER}`fC`J5m22c zz9btm;w)n6s*N>e3|?_TD9pd*73h{!Ct(0bAN z?nON|Yn&kP8yC*7UYtfDQ}~vuoTjt{a*hUHsxjN!GK22}B;@DTd+JbD`FeDL2T_ud+)9(>PEe z->Pz{Vv5Oj^A{4)*i7}VJ>GMSLMB~TieUa{?CGuIH?4;5-1Ac~lMd_vg?*~jM{y#| zdVhBpD;#wRrq`)t@7WC!k}xoeaKOXZV{;qeK9{=YYb5mSkrcVFFbfA>al+YI(CsWh1SriUe)Y8%rt8f_NIkX;P*iHCSgM4366E!*~ zSShPr>qqSG>mj?227*}fOJM8?vdP8gk8?sXeK>YR@NR2Q|9_Vv75Z4J>29FoQ|wP?^7vJq<8<5v;Z)s9J;>bq;!|%K)!i#JuH|0ZK#8gT4tx{O@}U_`bO>`x z+Qx2^O3Rn3<7SwOSjGC*x8zRBPfhVK=Ch|k3Cw^U{NM2kn|aHv_9JA**L5_3 z6d$-cn>BU+m45Xl`AUG?Cur8NJ(=>bAvuEL4c|j9z;Hs-AFyIjZHE*Cj$DdNyof=3 zQ*~>2I|=eu3Mj}X0&O3(eqKT7^WjN&4G_GqX|jL%Th_40Vlj^pp$x%+kR{$7gxww8 zaHZqws1bsj7MhUCC9){$dg~eOMLTsd@@iLhq2E{4{eLyLPEh8c#eJ8gzWJSlw>0A+v^)zB_)Q&@X$uN<5||luKK!fpSg;6peQ-RVZ7WFAV6+O5?-BHs zksD)~a8h_GwJZ$V-Jnsrvpb#HF=C8=TZr|nXnqwH z)hYU>$3I-C=iLg5j0eLN1Q0?tB|bfHVekFYQg$}#Ej4WfmyuDi zkNtIi@%HfWU%kVzQr-VCy2$26!*_%ax6=@>YvKL_I+pg99k_D(cqA?|zU^`lWH1}Z z4qM33aGe62^)phY>ApdtsSJC7*FSzTJ2WW%9pyeZ)9xob?5xsmyhU1f!b`0y*m)Gf zhdnniszoa;p6nhFX3==&W9#(Wbpcjg$Z|5IIB9#Gn1+Tzfxu-4Vi&5iT>hj*2sLUQ zX?{dNfp28%ca5$V^XB#j2W)JQEgn5p7cyfft@D*~j=dPnW3oZhET)8Y41SD&V=UgY zC!fOImRk8>C-ulzIOkRH{p93%bq^T1pBG=)f>&EHWwVZv9E(OOtN;NeoCoo@%b_oT zlPzgl&KZ0&xFHM}Wn_ygl91~_M-pf?|F`*kdg=m~x_N>wbiLpu; zFwy|=iuChil&A*{_uLY@rG@LWd;qrjprTBph`*`__AD6nkd?Vc7$4tSv}?-hT;`UK zLQ!Nvz^q`IDSL0E8JA!CrEcyQ?pq)r`M4K5(Y4imQ;mZrz+G_2q2t+^#K&lABjnfR zx~Cr(ajshR`GIu!0~HV6^-I2oG$%7S?Lo*@m`c#ShPnr4Cmx45cYu`YZPX4_%5}|1 zLZW84Zk!||Ewjv0O~zzeYJy-=!8xnB`gh&8_2pGUhy!6KPVvGhRy30}`soTdN#H}( z(P38Mm3WV;v_{DGzTk@Xj!V(HFLx9Ahqovv@Q_#JAfR)WJh}AGk4kA(We&Q5xYuK{ z?!+H6*^0u<>7)95qpu=MS74fH4ED;(pR!rZhOkgpwx&oEDQrTU*k{c7A1Su!N&w*P zJ%|j7%39a+uZ!yJT?M`RQg#wJSK8INj5J4B@Se&;5q)x6Jj$HcE;vs-WvlI5PT!Lc zj1Xu3yNQT)o?+M7R=3i7MZd7cubs$?e#$+zqSv+hltzQDoXPV?mQ4z`$_0fY8#}wS ze0QYFQB&p8-Q2e>Jor6!d_%l$?xC?ky?bJ(jBSUsx?u8dI}GkBr2ZAy=0^R?|NO+ zO8%^|KhqykEctA<4i1dAIuE_Vvizv&Ek?%9X_Prd(WYK##Cu#F0@<{tiXUPu8Z}v( zL00`;I+!HWjAIO;GyiD;tg~p|=ivb@C|(Zl=B<9!x7_0?ZgqJo)2}=&*!#FrW1k#t zpo?FdRK(qNTBaWRNPn)@uapUBr@OYlY-D}2J2=qMY(L?>a^Bd>_I=C%q>0hbi_`1jc>Twn(bnHu;>c=O|_KO(cN=p zw|IU`>KBLC^i3m|TXNGLzl?JIv+{EG41LRFZfwV(ue%@v;@9 zKh~)lVnPE729H?NDCcSyl|m-mWLY&yyxq|3p0DnSPE+VnZ5ZOn@b{iHrQAQed0|v$ zIS=yMr9~qHTtiwHxP_R&R6GY>f7zV5OTj;3H?vCUyrzmfI;0n^^kL_Oru-RJlnNZj z$@!ymb6sVlmW1GNWI!ShQ_4_G4qBXzfqp{r1QQEXNszHQe2H@?Moe&Wz_RC-jk)#1 zsZ}{JmIre3|LAOO)`yLN>_}&Zlg2wgvNLo$kblANTz28pzm-O#W}v9tjdwA-a%4kb z&|v##xBh|4BdB53tLWkeQs(9ZKql|K(RsTH1ab1(OQUp061R*NEqQlgUT#rH?Umbq zmRrHw7L3)>prC0@C zNH(>aajp8mNbO9qo(sJXVH5{T7`CT z@*7?)TK|E6vtL^^nc?kQ$=~)%Pv&)i*UU`{jZ2Kg#s8|>?v6do5+~oBZWDH)ODBGt z=#SprfatI6)Kjf(cbkff0MgxSdYQfVd91kVY%g%#F$wvAv6RWd%2iMkJxj(7NNwv*9r*qQ^lIITv6`q`D-sU)$c5V<*4MW~cao(x3G z&ul{$n&}@um`q)36>`yREThDEQN-0I$>Jsu=2199v{Q+gh{5!rN?&V$SH7{r8hp}& z0yIh2ij;YW$l3*|I8AN2*u)b`8Z7QS%Bl96xc{UVP>W;)!&7Z7V=_zbW8T^oF(3nha1jX&mV0wSDPVTiy%s6lZSY9pF%U`0o??aO6oN_!}W!SlwE?D z5^5X>;eYh80}UaO6e-UEGt~$@d>%^O?!$PO4eCxKxmn@f85yr9rWF)l3XY7x^{Lwt ztiHHx-XweOD}9ZW!PMt2A!Kke;a@+}Uc1mY(eG}3GhZ~^GO-+V}Y+9#9=eK=CZPyZ5#}{)V=HZ{LcY8H3TPt z6BQH|ZA2Kgnx6m#xJttsXO!@1v}^16k_#zK(s_529EBt~P2lTB+jPgfj7P&MSGvS#^Af0bs^ycb!}Y{@6IONid!A?hB=TV&58XsEpRmL&E`s90XS zVJ3q#r{79Pro{1~&F3vegmxm1-9yY>bN*%2V?^x6cgkT3NDITo<~IAdUPm_O&f%4| z-*k%`BDe}sZJ%&Wh%3F~vM{ENpI_0P@D3-64gQrPNpZ^}ANnN%r<+@X)Dpnve!pR!a#MA7oJJ-#GNf6lGn zrG>V@;Ps*kl-@HO_InesW}wuNlPN2z$oaZO_Wr^Eu$hP)8zNqm_?iWl2K$)xC9@rj za&^fKBx2N_ZVIc}(ai|#MkC9Ti>_p<&hOfaYWxR_pj7ZUjDI|ch_%m72e1mZuWM2l znEW}^DZso!^(tYT$XeB!!hdH#VyL$*aBp^LjQj{ocf&BeUEAbB$-2c!O|%e*D`F0|`0+v=i^;BW3Zi=*NFoQID=aqA{QUilDaM}j!J+)9s^H$Xn2Z^mYl;4U>=+g7} z%mIIR=3_Ina9G8~>x+scZ%B+ZQ7W*aD8=xg5_HF3sKc#>d+21wp3>wjk_!^><*#Jx z^6HkG#*u0^Kj&%vOaF#tW~K>!%uFHvh?GP z3_)3UT9#{Gd)&_hMn>WyD#~vg#TOY zbgU`8nsDN?8|D2srb38_BiA2$N0U?uMdm2~by!kVY)Q75S^;m-NxSIx(DED2aKVti zOMS`j4d#tF4=i+;`#GgZi@^n9VvU6tZm-nOM|Zt}hrh1IY|(TXzbqn&*Qk>4q|Z`o z>Mp> z>TLu2$)PHju(^2_mqg-gQJIyw+&v_hsHKQ>LY%uts<4<`dEuzZ)UV-9#CP=tU2GN$>+P!dlHa?Et1muJh~tZ^zD2PbN=AyoVA z+%DN!a=T^ilChT_>a4tHQpX3#v{xP&UjBCLf-K%QvQsb5ji{&}(qsn3*}yI?#1PyX zr7Npri*2le-OT005c7xN3N2b1X@acbU4>^>9nj~>Utm5hg=^TddII>5DeBjzlNjZm zmF1|eR;`DAxtTlkp+rnl-wjPj8yWVcZFv^HJ^)YF5xo&ro-ZljPiLK?$LXrVhc$YI;Iscw)5FNJ5 z6Jt*w=&;$B?KG6Hfs5R3V~7ULr{mE)m^z3C2+V8w=>Ny^OOr3!)m0DU$2~>jVUMb5 zxOr*31s%AAefo>`@JT7B;Be zCS|6J$UPqB^8w_~PVTOYq+TZ}x>a2#eB2qkh>r)ANBL}IVFlJP`<3v8X37Py9r{!#F=Az9YHVpV z^x&!$>jxiMVAnl*;-8S>+>IO{Wl*VnfT5B)Z2ccE8E}AZ{np+PgWJ_yZ1QokhImta zI>cCjF@<(THfcR;t;4$z=o(G z@TCzn-!DJnDjg5+%LNk zbNhdh)9IRF!>F%~x6=1&x0$7?7Bp8)97vRV&3wEc+v7~O1vacw8cFyZKh&jg2ji%M z!(Op`DZtS81iCIVySJH9>0ZW6VtYJ(11cET$pPwb(uW1sODaK8c@{kpZ$#0BFvY(J z9V0$!hh!PuJB&S9%d~P9WU=eg9U1;L&nUn8XP5P#3`Rwl@d@e%hi?^weE6-d3vA5Y z4@T)1t(Gddb;|2qARJ{7?j6PtOYtBV31g+OU|>;Z!iDwsai7q$6Sg}*&+36~dSj?+Z@Y5vE4N;hggs7sCvMUn7@>CBZbA!Gcs8nO9tHSPueoG9fANVZ9{@TdCTApnCL6!GI5>_l>#;scAt+*2I zKf0LcR8D>Ak$=Cj%BN+sVqAs8_H=*DUB}fPqHp|{mzhTW{{VJCiN6+IXQQImb9(pa zzAK5-ToQ{G2Ki2p@0H0=+C$OW6_vk#qZc7U7bFv85YG%A|rh@z&0%=%IhByV>UA`1t+?jz-ION1Ew7CYih%Rh!OP zN~-SnxQ?@5xc;r_#j4u^_Ok|bD54N6$3)yMWigQ}PDu?es5GN5-prSVuBwH9_Y8&a z7~~dh6|4)%J*#Za<82Uao>{SdjJqlH%mvd6p}!!i3_!!}ZHJW4=t;_17?g9KHfp!D zvS*H3r!l6W%J~X5V-&C@Y{_jb2z4o7zTix&!U}lr`r%z#ehQIQepXz9@TjfVLs`@) zC4B0$>%1^Nm1+f}HBQE)46S^BG;rdiMahM8|d1_VRQ%5fcz$UX)woZxdFo?VJ#;f!O-u4?`rUPyK5z!saSx2*xWGdIg0d= z_?lJaeSKxNM0d;|70zadTI`7kq~yG;HrT$aI#Mlp=qW&< z4KB?uqX%kg&8iP3OeDOe9uYz7wXMDvKj+|x!U=aK)kE=!Q7OxMDU`f5G}FJWJwUiD z5|EIruWaI?N7@-n6FhQ%0Gcdx57$D6j0#81`b03Tim(9{FAmZ$U)iKZTyLWbJ@fUW z7%GJwL?S(p9$4I$8VjkRJ6LO*NCr*KKCV&=zaiP$(7eGdP`}shVsFbw=?|cespAKP z;z^^@yCMitfnf?TEw{rbnHVFhMAd~ua!Me{WY`=$faGMo8Uf0GXWi+K(+0hQZy1Vt z^{Z>*gx|){8+f;VF3jT=-{#0LxE`%pUK(&zbINwI z!?sa57Epky>tf3=ZQrZ?GBN@f6XEwj$`w2d3-k+~*J&e6+H6Q%~%nxi!M|xhWuxl;;>Qv31F!+S@ zb1j;Q_|r*l#8tYmp5cs)MZnmQi_@}w>URt`^#XbS9!FPIM1Nv0R{T5+s(708kaqFk zU1)xA2aRig(oja*uq_pm4(YBfl;$&0MO^z^dtQTZlc2(^>;OZUwp0y?COtjML;lNF zR&qRS>n2TUKA)VrpwC=EqmgJd6aw}5N5_HJFLE82yPs1Qs7>h5DCzOfpLl=&qB|R= zWE@vzTGfO;ZA53dI6hN@+VGu(;vT*{y#`78u~pGHb1N<v$^mh2R`22j~PRPGE`-q#!t^h?ey z-G1GDxke||+b8MOcFHibvH?9$OZ2+TsEPyZ?DK+=)x%8&g)%7GFIiIBSNJ2Qu?-9A zcubz))pY0TvdtB9uJ%1TL^LaP(ap$hH#JT1W7&_mH5Wd>4txy3{XB-02m}=dlMUB@ zI&<|%zIk#@Tuf2{>rH7(&c5I&eL=b*Ojp)H7wl~gia5&<_f$?NR}JDB)i{xWF7eVq zIVQ7MRi44o#)r2*YCH{wJDof>GkVvkua}jSiiwb$6SVyug(EYA*FP42%+=FsARDjE z{56B=l=U0yT4Ax?`1#SqFOekHEkQAVxW9<`SKPR2$9S0Sut5wp4)d37+7>VJ{)>aj zca*IuKNPB>eDyl}VM^C~_~4yY`U2{lY<)e@T=z-bH zyw`B2)ZYq4KPe)D668zu3*ke*gp1{7Z*XYPBGFTPphngz-=BslY}<8Mj}G>KJ%U2A z*TYblw6|7uARUx*4p)lB>O&Va7%(^VX!+@x=zhe^cRZs2u~vQCnyjt^x_VYcDmnZ% zhdberq-C65;YjLZj? z47?Tb16S)nNL>|!%#d<;xTFbxUPm&U07ipF1SBOuR@j>PO;ZA0eU~SHDPMbS*5&C+ z+)SGfma%4dU{TtKU+Y-}g)?d}(U}wnl%5!Z#Tjz?i}E7xYN!olUPyMOJI{l*)V0fL zsMCZgC#V@Y?62PqHPv3!X{kR}9%$QtW%z36oUtBX+#o*JWFhqi)=xTr0A2WHY(G~^ zkJTSml%5Jf>e-jZTice8C#R97e98vjq21fPT%0`1EF%)B;YiEfXdulY-+qhpdNcf+vy%#ad^WQ3oF-`Uz8Qa+-SiacUAVNZmlq< zWP4s31dn1t(z!)q6N6uWEd{7#(3^)>i+N_~7-mw>^_x7sh>SNL$2eikkHYfjsb*d#iuCs!*fAHNLpJFzU&krOm!qkMv*fL+a= zYfS5|2DbYqt6aKNiNA%jryO?6ZSR~%k`5*%aeQEK^r-Pu0+T&||140zedC<7eUf=K z-G68E^X+9aO-S5_pPpePpBZi5$IGj$)*Yu?rAb8hJ*r|Q$%8eWZWs4S$AOu4|Ex{4>yI(Bs9hw_|p;aQ#$b5h*YNC!iMv!S3X=Bg%FriNWwI6aL#{36Ho zd!*Ld6sbZyS2Qi$iQ;8sl(Y$f!EC@U(f5M7;om?ql<^D5Tdogp|9%s0fM@)5CjV zg4e(ub+0)P>DkZ^QpBhWWdHYXS;Hud=1R{h{T73gWK_ha`GNF^JKWcla-un_%a@BlgUP!YELn<&=Dw6f!_sAf*Sj83= z9ou^x!eRCn^JNwIINC8W0E4590cU}VMFbI)_+p}F0QwkK}MnLd5(8uop>TW={VM` zN^zyhqLAV8PIh5lIItQAv67J|^CmG_|J_qS9NA3}S1^ zVc;Q~{;I4qPeIJVjnh(61oS_PEj??vmAmu99C%peZ?Z*8W8B~5LaLt4cY$b+YchnB)M+a)6rMcFP9iBo1d=a>a06I^=Ju6{iNv0%4v2Z)MNZr0m)^W;Lre+ z)bZJ7Kok$=_QY8!ii{g8->WWwylp+j4t0kcgtc`A4R!XVXUBoV%Qi&HDVchN>{!+L zEXWC`#;Ybl?9D#!6XD5}p?{8zNjvu&#aoMCOe?;DX=HZ4pq~7$Z>XE~D*g`_Zo!b3 zK{W#u7%?C)Aa7!7AUr$@FHB`_XLM*WATl;NGncY$1Sgj;ya6da_XSWK*wzIKBMI)o zwQ+ZMcXtcYK;!Nj+}$<7-Ccu2aJLW$4#6!rf!8y4?j-m7|El+jD(JO+?X&ke4VOS} z1bSR~{`(O?V@eBP<>lpO_}d*IY6o(*G6mWL6oIamAUkkIQ=lzC-NDofJw-b~!V&JJYn>VoiBe|-{G&LC58-@TasIb0ii2M>Fn{~~iMdo%ODXqdS< zGHcpfIk|ykCH}Diix7UxEI_URP8Jpx9(FbW$O!=QG__>@D}jcWBj|4>>tA9p2R|Q2 z2S&N_3oCnsf8Wu;VvzZN zF!=nPtvmtxEa35D1+e_}^Y14E@PwH;*xP#jHvjvGnRV5q#o`X60u0f3>o%K=6i$+FRKE+ej-H zDJxHqnTnOGspa1j^WS3tN4K@I2dOx?Sp9Y102oCf8aaC1$(d>xzfulxCZG#=IILW3ezyXg0_gfX4%pM=Hv)T@ z08MRNY=JJ8|4Lcez(txk15H7;AamD0#GL<#|Fbs#24Ve=f7k}(`akx(?ElLDj{~?{ zrvD;Na7CsLw%`%_mj}mR5<9!!eE*s~=HEow!L2cKu(bs`|6u{H0`xm5*aGxFNxA;g zJGp^(_}?ht`oObi3$*(q3b-NWzoURH%&px22>X}X!R^nqU=xeqNx*uGzaC!DpC({B zznjYeZla}^f1@SH{tpYV%<2yaF5dbN2tIn7KOneYze#X`8)N&|3jWrEyZZ+t@D0rT zI~6B5mOXfNe>VY~+TP91f zCch(af-80b*;)PXWO0IBT)^+yzlp)^_&>AH3NG(=vEVm>+11h+^v4K*xw?8d{NVtO z;r4sB!BzbI)y2is!THaIfltl-4+t*L)#I|_$vM{{`*@G2;>PeMfkepV9Fn2-4N1sUnff7!MHaiI8V8!ok7d! z^VRvY+an5WDs62}(1!D^XzFM`+S-mJ&5g(^$uFPd_9ocyj%3x&C*NnIM77zyPK1>i z?BVI6W6{PTB6vbZ4Uv7{UrxSSfi}?XkUesge|b)BJSZv!@9sSYr9B&mD!xsGE$yl9 z*K)}tK3B{!#%jjA4=i1v%s2VCh)V|J%18u9hjHPFx^QzlkC8k5N+Op?kKlI_%UrDqLvZ;M3oR4G#>5bb?|2XC ze<`27f-D9Oi>>olUg-%6zs^SHK%D_3_?$g;V@J9M$77UbTzYd44=L#a<+hSR!~KzP z_`3*adNW1$cvvMbpZ%?`Z*FvtZ{&|p8>aQW$Mns&;Ssj6)>_5ZC4PlF`C%%;5dPff z>GdtL&Mv2@_ZhIBK5?A)-1k)fJX|Sxf98gQ09%xriHi2FD655=ay0R#{vF4A_*&jh z6A1YCeYEU@#qO zv!M4e9O&QHnyyV4rF4TCCBv!9yt*Tx*Dzf4qbNUoC>au)KytEJe|(B6FIE<@pX0~k z-7Rv=E=(FKuBD@md257{esvBdw%GIN*j24mzp#Z`rhIv$LF8nDfcm^jP+RXSYJ63( z;7!H0{siGb@>hd8@(&pkdh>mHe=A;No+Gs}4dL})0=20m^b_X~A+o73n)f;u`N?<* zJ403c9{jo!H+lwUfj3LmOh&(^V)ItVWk{OW?e}91kM4&(;z#EdTj%LK?b;Tx_jPO1 zF(-sh){vQ;_uf?{IGX#gNc8%0iBuls9)9FEIAX5tw)CBzVY)9>6Mgf2f3!}10opFd z_SSHXN_#B;Cwl#h4{>Y3#VkQm{{-1X_wztn)~CIXY+d@UGjpS3X{&`XO*z7r zKQp7+Omenf=)Mv`Q3N2E4Hp|nQ2+Zk6Ha6NnELUmzr+bU1lBNz z^eG04j@82B?trZ~pM$w1FSm`oN2GH5eI2*=H=y^?EGyw5ZDcFcX6D6pV6>!XkS=w{ zNX6X|XM~igzo5N!e>dCq=S7$$Q_LR;9;`^Q&-W8Q*aJ**Wevh}qWZkr8u;QSc9G4O z^2^tbqLcTIE=!)u%q>nRdmQth1gtJel!VvA)C^RXR?r|%5k1ox$%>6PTdARd2xf2B@Ms18GcRQ8#b%7`2Q zZe?qa1U}tQ^K-7}6sg#WE{xFUX)TX=ua6ZbWt(D?gyR~s8)~2+zbcU{?2aEKSNA?XSv zJVxiF361=)f7C^6U8{gMs3BquHXotO4ye%lrz$1MvfVB_zv^y2mK&c7q0br`6GJr7 zq*l*U!`Nj$5+TY{qsnj3BU20IqUT6%swgq=DgN+@QfHTDQ$rEqdY`)~%y_YAh^-S# z9q{ePjlUE&lapn}z}q`rg50Yo+aO>?na9Ler5a7gf5}qg>gu1{S&F>%FuO*Wau#zY z9;DmpNE2NygMnijb14wj61~tSL8E-yPNW9@R>S!ooyo?pKZ?rVz{XJT^&4+m8p8A= zX=udDR@%!%f^2tN3eZ1KCSAqd4dfYPtluu}1ZD)>F0TWq%ZME#Hc)5;-&E?dw(s9o zM&@buf6P{uF`p8rP}PHvOL ztzYVw6jPwOfA(2C9WIzLLqjpaI-Gu>8~mJxf3elkjXKr?!gRbJZ$@A}Yw&YG4UmU8 zp-DUU5yhWzQQRRi76zareQF-}!$b4MxXQtRz$i1ETP>w|T4n8V&sBufd0r8mZL|u=dq?U`{rA{IW2xY6Iap=V-pUv6 zf8qQu1BH@Iv^<5PIj+X^=~E6zUyoAYYO0X~g1s?~_ep(U{HuX3L zWeF{v5|>{hi0N3M^PJ8CI_d*zg$_cLu)b)*53vuEjKptpqYeh}D!aDmv-`$ga5}R< zU$;9Bue?01@W5`QYG)8SNhl0`?uzqSe>*j9z)T)7^GG4^T@FE(mV5PPZq4~2L*1=M z5fb?oB6CG?s)#4HXfrN;SPyL?A`Y?$PJnyEr&hu7JNm$#7A@sFjIAFwV{7ceAt#v{ z4n@!6_I?~ft$MG{BXvHV&NzLVdat~a6}d|b(luLdi5$GELYu}8cuw7u%t1w4e;*D} zJh#*0)kA%A%y~(ctAu-XE}7g$#5~QVO(dV0Jhb_rLNgs0df{=3#T0C8yCzP@U$e<77;D7nGV^S-X4a&V(Zdr70x%=W)*Y#^5fBKP-3 zkyU$-ZKF6S_Zy}7R+;L(5PFLh?FWmr>II0Btam?biU12io@sjoCc+5^LS$1|sgSRu zu)4jW=Vv)fIFW}MB^F{$wr}2jjpfz+C3PfMD@=x=?`Y7ExcYX?amq4gf2A*PsyC_Z zGE5VSCt!Lp**pK>d$&TEaAUwDR5H#pAF+Hif6dKfzV<8SgicDx%^Ql9a6TN9QC`QL zCMiR+EU_u#B{?-3NG^Rw4Z2ef(wx{?FI@;@YQ^SXG|-6EaJ8pnitj2_M9GKE69Y8J z%n4)2-Ux&4(qD8+sgl&Xe+MPM93vW(u_*)`g$+V*y+OJZwxs2un!sWf1EQG+-=-y# z;&iY?G+(%)wJHh>MMLYbKze^>-Jsy2z-sk-3e* zlq>8pxKHJ4xT*edXy;qM61?;yYL;voFQeH}9~+-!VM^|)jbr^ze~W1LgdH7Bemy1& zGs)i>pkjc7@p-iQvK?CuvgalB`A2GpEHpz3*@$rV$K=4<9TTzK>8X+DWgg$PZ_?AB zzOSFZ=kJg~La-m+f-z-_*r@d+(V2q5%wr`L=G(;DLT^voL&J6@Cmz_u7N%^Uy-T$S ziBj(Y>3e78f+itCfBgVs8QFCC(0pQ?l<0UMF}D%!SK;T@K&Bp zro-#T&fhSooO=%lkx^t%j$kn2@#0t8jfBwQ0+CtOkJQcH{)E8Y)1^2IHKr}fqhaHDk!@{Z@<}Gy433G0e-EK=`@AxTkSS&cDxI>( z(W$56rx3P(m5||~wPawb%TQt#vStTI`WYmsxZ?p7uCH@`D$ge=u7g+w zrE;E)sXWUg^J;|OwwQ3<`-~a8t-tDHMG%lQm;R(Ye{5&(i->;?n|ZN+^?+I8P1 zY=zvVuGZyvHc!(@FKgc~7&CG?1y*d~cZy9T!tcn2vQ0`SRQpf}&%4{rOM)UB1D77SG1yEHg9LlB853NV7@T$FLy40^g*!x}zfzS{1Hp0Lp( zTzA{I*!55Ol^?xyhUQ1jVVgRWvM2IT+he3GyV1^#mp#qwlVJG9+l$f6c@ozX*_0_OKDkc`uwSN52LD1{H)*_9(4_rZc=em?i=awL`LaBJ84u zndYO5Eff^wa)&Uv^f>K4svfL%Uu`{_hmfNg83p)Jk^S%~ZZqY9!u_!jC++AkVB!^C z?OHX?Cq5S(N?3PdIx?#nOEMWC9 z)LJOpQ}bYrK95vX*O21iOD=>_^Ao*YK8ew@rp6PN)LYH0Rg;kA0ldy@(+oe;zcW z1mKh*A_0jJyjeQEZKVd853v3t~&?zNnljZjA&Ih-J@K?!p;`c_jx z6)UJ$m&$KCi9AFvSqn3EcF_S@-}oh~YZZ*~W>0pvF$e?720r5E?( zL`|i82X(!j!>ARu2PiiU1P_@zfRVehs}OZA6zqqn{R>dhSziw+J7fTyn68EeTE-ig zRhW7|Y>j*kV8&ELP(*I!G?Y13_JT!jN$0}Lg-(ePy=w|*e;vtzt(TIXOel}fZbn0;(71#w8@TMnMm=@| zg3!!AHvCmDe>S?A|J*J>xTKbaS9(*>xh*{U&dIvsGLsfzEbcgKzJdNzOTH+^msz42 z=Ys9daxN;FnG|u}B|GQ0z60FO4H(zb50rf2S8~oTEdt+ZDlMc9fBOfU8b8!}svN=? z2FP!JU%v_*B)t$5iGnNcU#dJfMIL}D2+JUo#K;Tt!MaLsb3gr#zbh9$ucbVLk3-CZN z)$?uX!+c92KB7NMe{2=C47vzLoFlt~cxu?}9_Ghq_Z3a_mfF(gxYdSTQ74oc%aGeD z{d7qC;T4lIr_@Bh2VGfs;Btr`D_6}Ue53t~WHo1@Fxe;VJ>LNS#Tr?zvskDU9VqJe zW>G)nAAY*{vBlu|sEX2h;L9Dk!s=|sVOg=e#i(JhLuq6?e{m+7_!56OeoPWLWP9r6 z#xoK13k}T8#4lFRSy1LWcw$DQhBD?OnO?a34As=rlvqaG0f}|VV-X_@NpU01kor2I zXzb-DeN>WzGbT)HnzZ)z`*m8@EBBT>A}Np9*ylJ~$Wf*>Bq)a);%V&WMYjld1o_qw zb>T@CJ%I|Mf37PN^#e|?z%m7CmUbO={!QP%=JFR+M_ z`subNvEjeuLw0_MR@@U8|5*@OlXAl{N9LSG;F3M1E?%<_CD=dbHHP%0zwoqRC_u_s z!b(xcsgUwr5glEYQRoGGF-7(af;HTXJO@LL_lzo2HXb^ZydE$;WD&Q~VoKL3h7L(z zR*khLf2D127N+Pot)}aiX`%w%8dU9`bzmxNWT9xospl~0u#UM)SeDxuJZGL42K@nN z<5ap|XyJLK`FJvdAr~#LDr3jOX4C*mlILBkFmy)liZL)kiP_i@rWEyHYWt9l%?i5g z(RCc2hFO15WXG3ZykrBy0O-uMv#4`}9HQOEe*o1b9vuY%p^SYSsd^HrYg~3KpOlz)`zYWfR758Ysog5q7(m-B;clb)0kMR~k*ppS`ze75Yq{j6q) zLmPp|q6N_vds5$GtZ$-Pw-b>t`b8!6V>#+Rr26BMgY1&l`$5=m23AWN79-KI`bHai zf6YcG)?tJ-g)b?bMYUzrN<1E@iV>8s^pbq;F$hs(WFgRP^A+di{#(hw5>IUYXBAyZWhNQ0&$Dc{!Ph%chl+5W^t8p8XinKv_2Xet!&TW`d@;-eP#g;i&|x3m`~w zqV~}Gn%n%cR`vL{HkTTDTaM4vx`MqVf5@B-K`E)3k6m0mfnOPI%$-cYAA6)xRG3$= zXCWMzG><#tgZcQVINll6a!ci>uy617vBV#@nB!Znmj1*hJyU^EsL4?oh3jo)0_zb| zi{I$dm-#-^+2>lH-AVz0Z1Te*?*K zOoCinGRHC~ybfn7mh!bu00!D=xcy6W3qpnKJ1o$j&~N-nREorsTD9qj^u$Mpv!j$h z9jONfz#p?T9tJD|<8eBIrj~j;VD{8Q>N%%U?->2KBZJ@;hOxgg_GQ!Iw8@Is#QU(0 zw15|qDG7mi+#G*a%+I|VyVFnv-%c=XGKSt0^t77usNk@-1WjH#B_f%*+Q9Qw0BITs{(Fj+aXND zb1oZfEDtCOkbx%}e}kKY^l$0gz`KJVQ|KZ2QT*1o}H$u znAlJQCEC;?-;hS$SbL)>a=^Lhw*mP3IAIRypr*$Lm@*O-)uHe%BTKe7G5z0IYpzKF4e?OYS+ZuiGHSjav*kzSs z=1{!k^u3z*QRpw7J zpC%)_;#X#gIM~l?3YCo-$DmG#^K}Z$ZHP(OTGHf!bcimgmRWtUd*81?<;d_ZYf3xe zE9|FPgy|Inw6Y*O)eG?#ksfs@uAB)__(Q3xAJU}Qe?35KRaV3%dQV)Srr^qQ#fHB( z@^jElV|E`6N#=R-8$u`^E$fWFHJZ>jSYK=V#Ni{y6-3*vq9_Wo*(-(Tq3r!m?hjoh z+=NMc`wj<_O9YBN%u%Ek?foH4Mf5AGuSc85%*3@;9VRw})=M&8WG`0O55Y;@R&sWgdxC#C}AQPiR7 zFCfQec~yZL;0Bg=ml(Wa)jIA+Tr;*b*S@bdWPAfr@QPlHGv{Q(N}Rj8jQ?epn+DRU ze{xA@JAh@k`)d}0oO_kYuxX=}?B%6t$Is(W=Y@w%-?`R$e?kLKl+Ei}tSh#2B%d`2 zW~wNXSov-b}} z&gxw;tNoi19)H~2sTJBns%=+AR`e5@f4r=jTh#sXF3ouKxXg;N{P=OQr{8dFm3`-S z$=+H-65|L)g={3OzCgk`D2#k3t(JZ9PH9{5CI4OBZFL(b0@r~&>psP%d;%ok`Pe}@ zv^1rwszwOXu~kabP7%|HvRqgfTg|fELRB^XjxTaHcuA?`IJ$?r+RZv;tI2$sf7Il- zQrvHIf#8{>>nS&y{sOuCA*J|bzNyQ@3-gZTv%O&-@G~b624}c<{!vt%GOxx1-CYUF zFmh>tGEhusgc7D!Bd%5&F~GDjBM^z3+rbXpB3+}yGAg&D3C$l{A8r=+ZtQ;o2W5t2 zkn)!cL5p{6lme>l``aH*cUJjHe;4dL5qR`gnTo1JsV`^NQeH9c^?5#gnL%Q|UN!h;?LH)qD0w*+z8U(wp-I*ZGGQ-;Xk zk2$o+0LVvq5mY*mIMrR@e|)Z~vh=hO&U&PdLHCK=$$Q2)-#1p3*bp5<;TI z;EqHSgA$If6~#cW7L^a22U8G zJP8BD5O-;=@5knC2YhZnv6)P4e#QNpY2i&HWgC2ZtR88FokeuG9Ts~i_Qj6EF&kG} z<$Iaa&`iih$LtVvnC%+p3a6z-SsE?UfSx=*51e>3B$t}b!Pj5W3*U>_xKah5m$Y4- z7b=Da8Q=`3pa!(>e~F*fa5>0_9a|H;B^9;D&C;?*%^dPX~0@ z|Acsz@1jp?Xj_wTlBqPc^(@e8QqMbo6!gDFW@Vv^Aicb|2m<_&v3{FQkQT!u51bXk z8gi&q3=`zVsLx~kF{cYH6|2f1E3U6*M5XG*GP)B5*w*gse=!wvZlIuvq_W6z@cvW7c~lXZuVLA?!ZTrq{vjsHMM9pw`Sq8)*u@*6rf zi#BYJSCVvjT`BgQ^Qzj@mq^281oP5<)U#0Y}C8syJpaO0U?w~cG$7<39$2MZOTm8NmXfs>{e@NpyjP&J2Y zgEF{cAGpxUlyLM?VAe0r`Wqjo89NV$b>qj_R-{B>z_98ofTksfb) z8wy$#e;LLZr3|oG>h=PiWNLwZkQQNK^o!~Dc z=Nt;L2)|uEGkwEhrZhn@vRI2P1xwPHR*P(D*&wVkkj$P_#u0+0$^XeU@6Xx$*WtE?m6rY}IYUCN4Bzl{lpzZ$Q zn`}<)7)fYGl?H@AhpwS!I`X&U;#jB9ZeGTjh(5X%FM?Nx`7p2V%KTag66GLYAOBainv0z)~r3qlh4paz3qxn30H9HxR){G&}SAELR-K>Ys zNQVrz47rT!B_RC*CrRqU`F+*&QDvsi^C6+^{U;oxgiMOW*7p@9OGsHx^%M)fe>F!M zz?V5#TH=w*GUmF0i>s=isd7k0#XlU`22Q!@plaSz>8WlgS|5?)YEdz6Wx`HM90t?M zG)`HzjO@I}O2;Ayo9u63u?!V>^_*XRV`ztZ*CWn|wTPmfu-Zc&l4;r!A5}Pm>-Hjm zAvHUgkZN&$*&&4Fnko;(Oqe;a$Xhr*JaxZtUW9!Z{DA1T4H(pMWZh)w zckoa2%-+M%y>ss2=CQG<43)QYW&CZiqvtMq2kNqzxC#b4Nj!&PyE0{Pe{~R79?02c zQbk+-Z$mW;q3A9|f%FF1IBIM?Z!cJ1=WLE#&?iv6Ff`EW^R$wO-pwjW7X;TCdAqYatygyB93!R)TIRsW<;NA(t!glFLCumFig zTaj(V<46$r9_kQYWzikz2F+GN3o1lJ@pZQw-73>OT00F@wq&H8e0gUSk#M2SJjO^x4SI@rHEuu2 zCmqrPmBYnbsZ;q#oL0%O`PS`!;0k|#_c1)}tO!Whxm;Nne|Z7%{!QM#0nt_C6&Cp#g#UV-f+jBzCieNRD*$(zFO|_QxhYbFzE|G4VV)Sc^HksV;mam_n z{U49JoP@1gITzhMeFpLJL7U-7x&_SblXY;LL7oi-g? z0|PdByuF^Cf90}EcW!k&h3KYBPs|-l&w&NzZ6=hTwLD@!JJ{JeT{G*wqOX0M{igiw z%(!P1>c&iy#v(RSsl(!05hFAdIokdHQ{>THoX>T9mCkhZW^ebsqU~p-55<$D8&ZO> zXbFf9+`fqqo5-;oizVO59ww0J90+7<+LFpGGf_V-e+ER|twek&8R)K~H+`2Dcwtrr z)xZ02VSz#n(2>u}Ba@k?1gy6W}8=zw_#8aXr|{BCfhlaR_jO*~yeZd`cKh-A=9e(|DkS5Yq$!hC^NHp&Dw$ky3@&^VhGePF zsnUjTaYQAOuI8{Jh+w0h?hv_^I7$>>%u`w^M>IrcVXV$E4Skx~O!j7o`i%zAlGc$i z)P}mVy!meh%X+FMiZ#~pKh{&F71Tve#8KNrSl&o|HnwELf?0fYAQA14{*?fxw<4Pa zw-gEgPXNCTK=H;nx8kn^y$%G1;iT@DK{W#u7&9O+Aa7!7AUr$@FHB`_XLM*WATc*E zFqhB11SA19laX2}e~okrG}LX_eg?^?*BVjkXP0ee>}z&evt<{JnZYm%Gh-P`*$G** zl&vgLwow$a7s(POrI39oBx{QDjrRBb|IhipGw00Ab>G*0KhO2tix8igrKBbj?}F09 z<4BS)DX21_ZF2T3Od5bfWu%}`X|RycSu_cQ`eg?TS)&L5Ja{1-!;fP#~#HXS&L zN;JXa07D-P0Fwb=^2#s;WhfMohC-Eo5%C0NKnL!NMgk^MfFT}-B7%jq@m^#C+RdFr zJ?Gb7KooHjfGH^{NPG_mG_fcG8Ue=vCUBBF3QIi^0mlHAcmx_nBL6FesH!`O}nM)JX}z4^46hEKo!g!54)D_9O#m;8@g8X;NSz;H*2E_`_j|cP073 z2`GSSz@QN*9FZF0gF~VS0QGdh(!dxn^+Mr(kd1#3B!Hif1HhzUztjDU-b;kWeGi5s z5O}N?97jgu+yGZJ1_hYv8B3A;NfH1YhukB=F+@Bye;)1&M`PeFRKoYG!vQ@_a{x|N z_){MdK|p(vh*Cr}W=|tzFAepXb#X{-JQj<>k%-{E`{|$wC^eagga#KfU{&T)OROr z&rH=2e;nwA_X1q0GEl*2R}}RJ97u%wq5u-X2NfLnx8vUk41)nkG=c=Upxn?n@Sp5d zGs^V`rq-W;_6HoG)b@h`(7n&^f9I$jhQ#AAj|J^$YfY9)a&6|G&=P%ksZA|9@|!=Yzp~_ly1@{6Bs; z7L6hQWKg@=heVwW6FhZ3aQ~&+pniMihIfN&5rx2;d`tCweC+goP>f#W1 z%gF<90s&42L#h2GEhh&A!l*NfMEQSjZ2%&L!;`2X099Qu;EE@J_ev@+2S7CUOh1qU z0MY(~6ak3NZ=?VPAbNk0Gyu{6gQ#&Pf4`9;m1*_|$p8?`-$;qdg#STG00i+H?dgXg z|AH_8g8B`qoeH4}!N9S9F{n1TzaUi%`Y%YmKjsgl4kHBr7o;li`U}be5W-)Onu_=r zlm{TBzaTZU?|1mGVjz471nQppJ}=Zt{leduA`0b?LV#bt#3NK9Jnlp^Y*93ifBH$z zJe8I`)Ug`7e!ym0m)m#FJWN~l^O{US+>mC=i`lr(<7^AkCA;@MZ8@QyPGdn=BeCUu%Uul{2)N}RqvlfdA5n^r*$um;SZ;Z7LRMbxrf28jnNHP;` zP_>iPjp@Ijo!Vjk*z5H&z8&x+bn~zZHG%KS}RmmkD)UK9Okq zDL;SY2E<;BvUtf21mFtp5?cr^gnBk!d+E!@P9}{9>7=eroZ~s`@MM#Z-AYXgT)-)6tmaxKSCHhFkuFt~J`(Cs{08|73sAXHm^s4ie6 zZJqWmsiiA!Y@JhdX3@5-W81cE+qP}n{)38bClxyt8x=dL*iI@o{y4YxJ$Ij{`##&6 z<7KurS|5G%uK+c(!8S-#tgdW3$GKPcd=I~%nr{er!Jx=G)eVDX29$&?ia( z3306+6O_D>Z`*)0W^?X6HCO7OY=IfV>9@1_B}1-xn8@KCK}OoHf)syc_PyuFF|t3C z?(R36)iFR7Mai>p5znYh8QBJ(^nP%wkJC#i^d0M$`C3`ND<;==9fSZ1NJn{aPI45q ztnK^ivGvipq^tsv06j;nv+8azPq0FIRFgD$A|1qLc{p80S2g#Z#SL6IOhhpc@PhWR zJDJ~?L$Wg04PObj^xJlV*Us}dk4a3IzG&I6L+Ny1JA}CS#t85_bHShug(nAL^am*w z6|Xl{MD;HO<~g9RTl>pGzJg14!-u#t&n>Z2xB|4>ioD1Mfbg-j_+z-54eLz2R<@^W zd*OgxFL9co(D6IY+Al)JUj(lO5MSF=L5pV<@Yq>~+sQa`%JTcpomvX& zckrZ3O3Iy$0ob^9tWV)r5-T*@pTNfVYmC+%t|Cj#5Cp~seFw4|4rTG*w%#Se2WU1s zUEb|*3&%S@uM`6eNzgLad>KeQS#S2iBf0OlZcZIURMYXa^CAVhLfLke=kjcoI{^JE zvzVhpxq)lvO0?cEqjE;HHo2o_CNJl{6B+Vi@lR4U0AJe>)s%-~A4*5b{YiT*UXw{* zWI?B*yoj=z&7`s1Zcugzq8t6le^ke_=Q}&>`E;S@lSlu;1YhIxxy_RGMA8(En~$I>!W z0vmlAfF&4@Au(QB3%#)LvRCwXP)uum{R&Q;qIeYJT`O9fA2yo%8I1jg#lYV4M{J_)Lw9!n2;1(t4wlD8i#QhL~NPXSmi&%Qv{{BD>!-Q*rzbZFeQ7`rM*%_y9j}X zP-1m*U_edAP0DgD-^!jl?Z3>Zg?K~+_!a#wfGil%<=Yvkvlip|V8JA$)?>k=Jc8fP zh2yfe?X*>|-{FS$QZY(@_U((-{?Rm7OH6YqIjUwy#}eXxS@Va~;2MuHT0=x`)!LsE zx$nRVyZm`y2JhOWw^w^Y&42IP!z}4S#3HApzalmliN_*vnRE9A&W2^}U9+EgS;#29 z1A>k()VFM|A(CB)8{IuL(eLUZGi#RTm7S@GtsPt0X9Cq}978-H!33asx)f)Ljpw-A zgvUy=Er)xbr*Xw;oQ?OY4at69o_kcZk{4EB!W`K&A~w$P^l+p!=t29%quzzCs7a)^A_huho4_CoDuyI4ez&28`24VpjE9VLkmQB?{whiakx ztv{rFHH>k{4fzBAePfb(Ye*w<1K^LC!}(FCpDf2(=o=?_-FK6l1A}nJo0Vwse$U5c zVIOwMch^&8-npq-icl$E5q{Fk+q|(>3+}XPs2q?9T-O-GiuyIlgi(uW0?(UK!+d1q zb>o-pZ=r$`PY^JclofO!p_R`%M5^NRzKNp9AhDOfu9d;m2Z~HETD6_GDNs{^|0z^Sbxg{{J3g- zd`OBkjx|4zI5GaC9tAoH#yMk589yYw=H*auB4*p;Fl618D;_?39P#R-2WM4O11GHIt3m!{EGOxA&M zJ?^!2d09};i$*Idi>Shi+3yeo`zmcp`6CZGKg_fn+YoPGERf)w@0_J}?-Nk$+q3(6 zN0^bN!e{*!urHfnF)yWJ3CT3}ntOV2tI)chB6UpTyc!-fm4b-G4aj(Y!fjSq9*~a7 zTxF=4g-JcUux-U{5_C;ViBNuzQ-i0gpaN%wHB)}$$F{MSIp!VlD9_nw<6+i%E;^r* z;#b&^UdHQzsT9wCs=v*+*UV(ehF+Mz87@{wn`xa+7dcf#N{S>z)-G5lP45o3tW8$t zJ8ZgeF5zeDnW2J-0vux%l2#0-ITvE?B6g*9Xl%4^$f9NjiI`pHX?6?hnEqXRonE=9 z*Nt5vUzEZgD@Mj8w_F_>cbn0Ep?_Rc%t~NmtVdMP9oC)$B5qZ}J^sqWem$))a_~+` zt5TrK_mw;h%k|``0W-toTFn~npvYa|ft`a@6>I=s~10kFoAXSfT;HouC6I2&5_ zT_#6`f4`LW&B<%;?zAC|=C^OmTg;?ZGng2^q=rDUDhs>?RN?&yvugcFg#>d)qsTk5 zC_)Ju9<6BGVx`Y>der0&+h0|O8SAt>_PWM`mZ^)!dh@GY@$8l0H?RF+%6kOGb(9K| zTTBQ#<Q15gzB%Zl&&Z(TkGG0SNq{+-&}zfs-*$JP~S*D@ex{V(U=SnW`p+IfPN zxn$cKjmUA4xQJO)W{zF^QLr6~>&M3kv16;Y;dD+mQGS3+SL9lzzpL4Y9w)t#j4F|R zIty0olB8;PX``q1XSv_^bmMkUN4up!gMFHnsnJqu6JP=t?emJv6n1EPAcwgY`(~!-WLLedvMoZ$+HjoRATpnjNRxNxk~K$ zy`~J1`vCQ99}fg{@n5+EJclR^A7|^Z*X~n!HI|}o`)0LU=<>EAh*L?IQGjaVROqO& z*TDp(Ap_C!1bpHboD1G<0 zBdA%$?l{7V%nM>8I)QIj`=|Mfv(J?L%h>|ke1OvhW!SgFjWK*3YUZtqMdkA2L-VOh zZR6Rmrqd~inswMy!)fxqAm>RjSmd9(L5tKx0fA`qGIQLl*wATKq2apMHnK=6G%B-YN0$km-i2t znHlIY(p0*nV+~95$HjFyryaQ9z!E z;tvXs;Bx^4&MAwG`v_Ffe0DOn8#RYkdkuyKw|(M#O=?bY9M@lYr^1(bN0C`C;RZZU zTMgutfgLZ0rFO-sBZ?8IpZ1YQI}Wdn{Lh3AMGxV!kQc1Xe}dsD1V_ABCw82cp+!TT z*sHn^ANx8e+^iY$`XdPzmW-HQcL!S#k%0 zdiM6Rx=sA=s=xp+0w%PN7U1ZH;mJ_N!RH=QIFQ3OkA4Cb$++-0GAtH<9-#dAsnV~* zd3n9GTXn_88Xvd8XVTh(01N-{^AYDH04Obl1>!{ESJ1~n3F;Qi*xA1Mn6n{@{@0v! z=s1XQUQsPxMjXZu^tIm9x68bmkI62DyOE_MyQrLSX zrn=G1BdKxf$DD?JW)QPSxW_(B|D8LkXg$Y7Ha(+o}w&$Skf0RZ+-FZPqA_d(br zEg7|?ZHK+VUL zAl%g(bsCmr)~>}8HL|M`(H>{4LvKYBm&wUS#FOkTYdO?yvnkWjxO9_m7=&`UK1WhI zf9!;ybw>>xD-O-eAgM!vq8%yb?>J#Dm|r6J=9qsY?blmo2ZXt;7v262ZH3xNP3^po zZj4PUxMq0Q<@NrG+JW0-|0_taaE0_vM5@U9zy`8G*XPoIB+`m|Sad?jObn;lKN)Y< z>)D>F?zNa)!+Lc8`)wd(h)4@{WOa&CVht|!O2~qTaI%XdbD&11riRq1_n|ShJ`C}k zJA+{IAVHXG3@~{X-T*^u9+!|jiP3tyik)c|AmduP9nF7SLyT$kwa|PIw<_hWPo;02 zI7m90jZpKtx3@{G$V|a>c)QkRN1YnRjGI=@L8U>~P35Gw1+;ke`th9lv{ltQ55pcv zpU?>3irFPJ9joP-cp54FV{8%JQ4Cp_wXrrK1!b2e9zYE#q1o1=q2QyIrCo!^hG9+D zlRta_D|#k?A$ha6@;1f2 z@QJ3f)Wma(Vuk;WEDUgouG5&&Au{7M64&3HEE#9t;k50(X7YFC8IRm&^eN&sKA}g0F&B)--(Cs3A_o}yuU7b z>feQK-g}%{;^KQ_gwjQ!6meMe!-GfcK_fDdknli=enpHNYO30_fDf04$(v$qz*+#B zNT6z;@CTA$Q`TgrqlTq?G7xaqMvA~8ID>!IORm+2kAYYF^!!C83#)>}N$x*(V%8h4uIAs1Qq6z_%le5VhdmP-yJpO}*0;hqZu~*dCLJ}?-X^ou* zWgO|PBs?^MJNK$0d1K| zPw7FDD}E`aLRd&J|Jx`a3`o%a!$Tpz88aPtIrL{S^A7Xu(tu?}eE7Zhh_E{pRGbKj zPB=_uWpx$Uu%m>Cp*lLuXY+*et8gi^l%vd_g_Ya>x9kB9sGqNfpl%)jVGWl|g`x%y zmYCm#^g664)M3Gc%!l3k(LQkZk}ri<{W|yGok2xY0QRqbZi~U9MeIem*w^n=s#mec zpOj!`7D)KGaPm-Wc(?skvwJ|%Hi#;eG2pH)I~Oc2Qs7$b*8>h|1}p}Qnj;CCd+=T#T#nK*{7|cE~q1P`a?0>P@ZXAReyPt=HB&sB8R97xDb{ zM7wp?-naC8an@$$VJS`Kxy-i?nY}=Pg2>Vl8`;P7H0F|IXcp2sGycA~CI+5r@xPdu z-N5Pr4d&=Ow(c%KFl{f=$P>=@#6W!d5@~(_CV5AD0%owFb$dGJhc3v( zS2t4=%Z#H??^)dyg8Z^0)Vza=u8OO1{h?e1DfbuQdpIhmQW zBXE+&cSMIIL^Ul#*7~HDpUyA|`qk7>r7S0FrMUDeeGcfEjf>WF<%dE}Kd@g4AnNtS z{JvvX%VFmMPOJ0ueu-CJ>Is<(M4PipiMAwyK*7G)W46FspJ>EzXQiZsf15Vf%raKCw48;Qv6j*8>TC$j}jH7tcfz_u&(e9Pl=ljkz#VGRB&H5UjpvTsV`3_o0RH1W~bBRY=!bYmgS9j9a~o{g>Y7z zxZ7N8%X8W(P4kvgWu&!+Xt}Un!QMNuHQ!6kwTHG z{1$Cj0xIMWZR*oLoShqt3NJ%IoD5kX9ET#0?0r{F^!U>@jlSZZq` z50f?J+Xa;mfOTcy-Xbg>Vxn9pc{r$dFl-E8`JNM6LSc^jg;s$?axUxfy) z5Aat!@h9w#^EhsOYF_>6;JHflCw?PpX0Gl*iyoPu^XRVpXwzeSVyj}eGv~vd39eoJy z--OMK$`Y#9TaJv3;T$gkuTIp9`vml*!;V@DE?7^1n1T7=M;D%kZF1qJRvh&VxgG*j z-r;)bdEXS&9X(?C+7^aZt-380u*nLvZyGdNoz1^mj?X+?`i$-_<+ZBEAc!x9_xap} z@@zq7Y7v%zuQ+*m^bL*Lv&R+|R{1~#>m_?e z)J4CxcbUS~cumlHL;=g~!2OsiWKVBDnT0as8jbzs*m@B}-lr*Lwmms4GJU)$B^o z&ufhDg*pWJcO@`0+_}Y#N?zC)sfpCJz4qwK+3OT8hDk<`vaHXif14~pZFj3}_*^f} zBFv86qtD$x5sgBrX#D`!UQXBNE7r!|c#0f%@%~`+;kAsn-$}pLRtQw^2A4VKBuu@( zH1ij6=laWpkCQs|Vh4DXZTq^5H~H(b@Zjtn!CahyO@R;jKT@Pf+b#2s9dpqs<8^tH zPx4Pz*JcU>=+OMk4H}cl7j9Y_KZifP>Ti>8-RTLIhc0%<6!QQa_=5>o3zQQNNqm3j zbZI+hM6^k&iu(miiC#De#nP*;GC4>Mh`lk*!@u)5bS3-E7`MT*$^Tg2}EOdU%SIm>_?MMc8QL;8%^+M|1C7JMJe zZoQyK2-sfwv^294=Di-}t>c+!=1F5-EaE93m)FmJJS4}cxULD~PS)w3*h!yk6jHec zEa(C0jAlt)!POZ0e6j) zgzg7t*hhdt4j)M)$H@;3{#{ce?^Y^^^sy7s*kgBO``o}=)*_X)vF9{f89JRKC&lZx zywv1FVh`qok|px$S#yfLW14L^`vr3^I)eV!a)%AMfXLsSDU7>pnHMgvgHMuBCuv;< z&SCL}iptO5y|@)Wk2VdVUy0bRGu8wA3iDv;kl8*(l={np4?{}CAL)V=11i!1}74E7Vf!VzY$&KHK|W+&tJp;A;@~I znRfL0W*2Qr_u|%s9d%!i;%`EJSI+WOv52_nu(b0v z#v%dP#Lr26Xp{KmMMbG?^0Z)m!W#Z&>UkAtGu+NuJecYzXG~oiLj}Ji41#n-Zxo@+ zT7WC%YOS48dfqy%+mlEGDbAVew#t@PzSqQ_@toK>5gY7$N_p2yaMuEX3l!~&-oGPz zTa6oysx#tI>yta~KTM8;Wa-nhD~WO=FLnWmfPfY5+1&=oiGGypi6!_L2k)znLq@bf z+N5D>tcw>hAjy;EqKfXzfLCTD#s0M}YvtR=@)XX5O0jKMDXWQVyK0m90{z9;-df)w z?7j1-ac!Pqx4n}Ckvm7PS$pe=U&;NZ*Wkq0(wSopv2)&pNBqc2UDcQp{d5bRn-jn? zylTWqvR%_CyTQ=OGVY8suk(8I!nXLYCNtla9%It9dF~&EVtb)lP98={A#)yg9gfkt zXy8A9-Ny>(J0mt!pXthDf!~eF(X&KIkgViOu;+@4k}BUuI7=DQ$&P?22L>iwe1`qz z--I}wE6h%I)}L{z0b9$wbzB~!ChvgQ?rwJCb9@OAbaq1Y9z?fSiWr~82mEeXWu)W6 zj`P>HZol0pGVaDmlp^j4JLiUYPYL2S4PFVpnn_+a9&}1oQQq?Y5@5qTFPQ!m*>hfM zc*}dbenNIr-PxAHSu?8t(3cmZf->sP4%KuTJC^zZb!L$65O=(gP<6YKN*92Y(iq8~ zqJduax$tf2{g8Ifep=JJ{`bxfbrw(B)VMG9sxf}&f^GTJEW=HO*;!A}$W*rPT<@P$ zJf6t>b+XZ%$Oy{)o)lF$`Q&;>n@dG|n1GK2HCJT2e%^N+_=qw>c@_09vZfGsBVsTS zb4TRL9FSMY)ikR@IibKt@j)W$AERNije%D*QBB0?Jw5Ls-Q}Yq#<`mS5H*;v%*+8C zP;^-C|2m#BMP4C@k$E{d;g}UHovb}X#rOT`tD0IO+mn4e-54UewKA< zODZ@(c;g@pncRSgPm7y#muEg(ZaHE-uQ~^1Q+moeayp6UWv;^*(4{xA(f>-<;5U(v6BD(Oyx6Y9lCYD08l;z8r;iXF*330MKur@n_ZM~r;KlI9xC*r%O#q2<2A z7*7I4WGa%|5`t#3x{(Eu4pGfN$$p5F!IdJFk)Hn~ZIGBkCI*}i);xszI+U_tYlnKWhm?-Z6DykM5Ifog zT#Ke1Or|}W1C-!6rR^h_AJiy(FXf-dh+9ek_Bu%=bTbuJC3XjNhl~^oWX97$x(FvSs{u+>91G3@Z~yDN`ukgB@kz zt7YVB?}E6JDkIIO-1h!d&sW(}P`52uUrkT1R-KPVU?RW)vW-;9>-Qh$_cm=uFk|9#)?*KVv)%{hW&YZgp{LZZuyF-uTPA$CJvdt^ZJEWg zXke6#?SjhQCQ2aezcw@}6^Ebn)iJ!(X2Qm?2_1V$@lQ&iB8sc@qwaY`F-*W69^!M_ zqPmQ-dYO}d#De;xo@1GibIqU)w+7CW5?$?0L4H1ZwRcaxDdXC#3t^H~NADY8@BB07 z26$p!_@g@mElNgZ|FLAVHh$&LSZZ7 ziseC6u$J6_%l8yzjnQ|ZvN!@b(Z$=ivGU}=iQ@S%wi=eN(7fJ84e(rS*DYP>iudan z`JMFI`W;=0VO$>emcVC8$5P>HwYb5#NEI8-0NQ`f-~)j$9_E-R$Ss#Ko5IgKBc;@9 z|CT44M6SC}>#uf8H7AFiHtJKIfAATP7N)hlEBmRxo2M-+DLAz&6t@7Z%({7n6wRD8 zO9+CG2d(TTqTT6PbO|ENR5q~`V6IBR&Zv0Am3y72x?+;!V&is{H85$@^g7_;Rhqb9 z;|kdhDnW5E;(z?exVKTvv>+&wkJ3mxpavV1sYYAqhDz;0)cKjh1v|vF2$qO-Q7TgE z_hGb%h2{Z4{L3DGnFI>ZUZ-RH%5k{gCs0O_!4rQpT`iZOCD|6+!%`dq>YSJW zwouNp?mkwBwCOO-vyio2WGa?Q29ko6WJVmXbY zg)ZF)x9%mwnm}ouLEAQ`0I6Bi5L%#W$4fBH7NLHGb|(OhbYRW-=7y~0+#eG z21E6eb#XtAe2^D<`k`vG>*#D|{BtH|_t;9KalBVZ3+!)Y)|saTT#yD>aM{hrKPy@2 zIbmoOC>M_bTAUha>EJu%3DGGhqVNX`66idAxG(^ z;1u%lrYZ)L9{(l8s|=$#*)mY0kT&p;c;tCCebZ+aBKY&R(x`P}B&EsPb1ZPM5}qWN z$G3`&%}b?kBqyY7J&ca%leH;y|{5RYT7uc}# zY-FbH%@f39Sj6_1x!1>CYw>k1Oms==7PgtN!j1_E@ChS_YN;v{D%e4lMtW5l{X!a> zTsy1;yM)sqZue6yY ze1nOY3GK;z{(t{=2}*l)^vrS2C;^VQ{+Q|jlQPTljzzpgb&-tT2hm*J*nixnQ>r4qME=XW;Asky@4fuUOVqVg!N6QL zVZ07oz{nj|&bQEM4LG6RH>lsUQZp|ZJq8cDE)gEv-o3neY*f{6ij9-kY_Oa9;vxl607<7} zbRtv?>bwo@su~@oG8lzJQvp2 z;Xr53$0F{;>e4N4I4JqfdrYDWKz@@`4<-ilvb+0zLCn>C0Ks%eThbyW8#I?K>VgkQ ze)uLhdU<5QqcP@^9h-OIKNes+a9B0~tXBw;o>Tck(m` z_Pr2u7w%yR)0B5 zhflV8@PbG~@X)b-8%bG(4nftgYnUOKJG@Ay_8dM0_zZx^?4h4%G673OsbD?#>ImYd z*a(R4X(q4B=<5>g4MUV@<5S43U&~^!1n)G_>c#qTu51O1(oyFDaB~+X$DN)!k*z+M zR;wx>3KSHdTW+6O=zi--Yt&@{d!OaC(Ohq@jFPJA>D0GP*?6?J_3J(%0K*??Yp#WB z)(^aTYilK-WELyEXw|jW2CC%u&HWnXDlB}Lm}Djvy7-d)e~1#FJ*roE%wF4S!?0k6 zM7PJCDBx%dtmNqcI~g8aqHR^+O8YM+!l%zi$NYLGw%?&+tb9hM8(WhwK}j(sZ(N}e zn|(u~ml?MvIB*rdcqe=U>dif}Sg+eAvC39fTLD56kU_EdDlec8i!axscX#tS`$5iu z&>Sl~LL?6Ue0N2fz&z{QEcJrraPCQ8Cuqt!gh)yBSCv@6`-OFr!I^vPZ%}mkOW*ql zBk6i(^)~G>=O9~i{ci;r;SygKeImnIX04OV`{o?%MRmXX=i9y2?)Pu+@7G@L>LTda zvx%lAFxNx?qsQ3mc|t@wy|d}=%gE||6o5Sq=KkyPV8S8j-Zh*U;&PcJ%-S1+NXSqT z`=O-UnmQJsD|w2n)x5ad0DHacd{4o~KX~Wr7rndwQP-!x`-^xb@uK1>=(FdmEJ&Al zDe_79V)dy-s5RX4_^UD{M*v^r}Q3MWjAHZ*%`16_|%}N zkfyLE;q5u%I2)6xm({26PP709mja`XB8l%m70?0(*aDVi@4QnE{Zo$SUk^Uwjwi;? zk)I-wbBjM;o({;PU5o%-N~74#Q?IYCpVprz%datSzum<eA1!_@F*WtK1HQi= zRw)4QktJF+j1xlpV}DnY*P(i``W)rfxxf_IMU+<{{+QJ7AkiXV7>l4Qwt^&qJ4CTfb@E;`1=K8xm#9K$*SdOm1ARlivW2M9s z>BW(E%}ss7mfpGMspU8hPfx!|;dKY?J72)FB_}_>(JX0XU-dLowcq>sQ!lr(jOJWn zLe=Ccfe9Xd|2~yfZtj+-0jejYU^UarIC%mS^Z72HGs*P?eu8!Ea!Y|B!^sh0b;0~h zKX z5oj|x+JdLjo>%fEvor9fxA&hDa1~ywHH5#Cm}0fEZ4whLod(hLvjzn_y29uw8<9VI zev11$0EXR<(@L_<(RULAmY%PgqBtK#(6s?3s8m<%cfpS+;~BX7Z6MieC4 zTH?PL!Jgh^TzuAV4<>{ymbcaKY{LQ|qFKci5IBV0-ybvz#oF|aN8jxnb2qjYhTj@F zR{CmP>N}zJ#!Bg2aez{ctO^+$KiqOI@!+XW<5v-QceV}94Dg>6!-a(%jRIs+T-+L` zIx%2ylVMoP6>*fQ=nw>2d2R3(2=WR9M87;w#{XzvIaGY88*b@LjyUWX^XA&J9>2mG zLJATT01*$Ou(rijoA^LUjkii8)!Bnl=ZE}@?F}Rr3IHpYVo3O7S>EzRsu1qV*n0QE zGd6YUOYl;U=pW_14&6=CdIkvNhb$kWeg_f@{}4Hdf^U1$K+hp2M-iq&agbbvC^g^? zJU^J{%O*~IiPEm|gt>_xzr-jv67;#>1Lb-M`)u6ax|p@=o5H*<#m}0tI^$@I@86F% z;Og?MN^3XZVn;pnL+{xcn^sD`T^<wyF6?&H?|LaCo?6~EN~;O9o42?8+JfN2f%KdI&f2q^uS6&_T`2_Uup>=4XXScAzG^_d=y$v} zfZ?kFHv#0ej6TZkEYCrX>=Z=KN(S3x;|MV^pq`rn=Faq#0DNow(ns<8Tq|q8BSXdF${~B~T5-Vj z4uEf#amqK@ztzfx>8Edm><-Y`ir+I zkcZtcuNyn?jLZ{|hTaRlm-8&R?F5gvi2*NkBZxxPq}mlu%0tIbZwlmqzS_@I&G(U? z9{|Z5M-By9iYEdJ!1`E(gNUM5(*zWjUUaNZ*GqZK!>${ib+C;M_ zH-F(x-1O|2@e_Q2_ZR}4_`vdY(C_260|z!igVnkkMY3Rd$D#CH7z=|i@vgXYZ6IkQRN)*st!gC`b!eYxs^sXq#od+iK>Qy_`_d zZMZ(lVmY!aL#pBki4{Cm@OTbE+Im3uld+XEr{90pV9{YY|H}>fk19-p#L4r&qA(i= z_y6@)1?n2cQg$K+-WaPUZo>1MPu*iPz{`utkT8%==&wc2{*i@ZTg=VwCKVQzKE7`~ zuBnbJa-PcY@JMjzyn)c>ZimEf&$}IR7D|h^+o93;K)}ZgVEV_Bc){gXiYp9?z2uto zl^5E#UQ;WhmcumND?HEwTS?ue8;dvF>2ZevV1Nr@NE~N`!GlABt*6HWqb=vdtfXJKu;;EMrzi zZ5&fd+EGmBB^kOeK1y^eiV8`N3}GoEJV2zyC)Exuo-_5oDgMWYgAbN~IU=qZE9RX!mi6E1wp|*Kt4tKc5%py#E#kxaR3V7e;aVD1C`Jt$Oqk>uz5ijtz*o#P8ywukv)Tbljx7^W z+~JpK+;*DB<2>*G7&@eElNd@So8K}5*gHg@j0Qm@o2Qa2T{Dv~CY4kWV|y+`5YV3{ z!Rm(Vq9;exkfJ8DbUV!_2niPuKE`SPac&Aa8TAMExLg62crpVn^f+qJAQ*ekI+1D$ zO-zrDTEe;^IaNpZ`8LB{(xSz#UYEg^UTBarq(P)E75;n}S|wY=Ny<1aG?a}1P+fB$ z(uUW=5MC!0o#$Hv^ZfV;RKEFsyL-OTJ_FBd6c+CNx1*Q5IVRj2;QJY{sci86IduU5 zns@~QnDJpmq6{GDa93;nD6L$qJf>P$%KeYW8g=RJDw5} zfzW3(axVvko=MF1;)4s2j~2_I35q)S&-MNn4sk^aQ!0J2YZQ!1(WlF{B!S7ploMGi zOVZBDYJdBZxzEXmcSwZ~M)a6^j!pT~FAxp_iKEbBnaz`9j%#&HhuyRr<+9B@)A+Rd zqfu)Tms53W&5|+c6LhE@xNpE$4+Pg{kE==7Xd}+Ih`h7v1Oac|69w!-$g1~ba5V?b znodM)7T(ACX$b2nq=>j`U2$a=EUsmw;>A-sa%mRPDG@Y{1|u=DBWC1U1P){JZRAxt zE2vRn?>-ZP^w%XLg2>meDgIhp(y{(TTMw|nnOw#;-v&aaL=hE-3;cT|cVV#J?Kjj) z`AHTDSceJ$5P&}04^nX#Yloztx%w%ZiQW*L~#4+c+>7_z5GQIpSTqpZ@UML z>ETy6Wk3$@!v0u`VMLvf4+r(d*sBcJ)StqsXL#qhLrc6xhA_^JQ5qwv#(NF-tay8? zxK+!pbC!Q2oE%o`Ox7A3xQRC}=Kz0q&$$_pn73fMALX6tH!H9%-6%D-(uXI2cD!NWZ0AIB0z^U0oRV8*WXUR`tW^Fw}7obf2}~| z-(&at?E1XQ-R%o}xn^$9WNtrUY!Pn>e0T@+^ll6FZyW8tS{q>Y=)tadby?d7a}uI{ zx%hv!@VC#q;0*sl%qa}HKCDP=hZNm?eu!~vmk)j3zHUq&`JGA{L#h-(5dnS{3F!TB z1NeX5*X_YMF73v5J3_9cD&-OdKT)Ph$;n3be4_oZ5BE0APc0IuqHsWlM zvPM#@o05_^nx&Lx3)@}GP8z31CCNPV+!um8G-p58-qy(;OL6&O1$9T( zku%ss<_%>o`9P^2H8539RpnCts9SG`=569?g9uK{^gy4m?Nt)usT?NVVy-Sdt^xS~11v>U8+rTnZ%!ZH9ERA4>HmoXN zZqqE4eT3yDm{Ao*mMLzIr^b<}%bjzi3rXs0o z8_V8@mmEH+0a>CS@7O@#GR8w!hks01UsKdTW|Ev?p>eB~$WB%-4fm9i9v=kXcXW%4=pE zNQOYKNcUfW<%W)gZeTF18=wu=aA!W-qRmj6)@r%SBAL7g0b6)Q#6|DAnOyq4tNM{M zDLWP4#9eh$=tp>dHEs_r6(&rj7%+Vz@-mizOO;N6`7$=VUO|2lKf!a%{ZE6(!~OsK z^O=F5kb0o(nQNesA&gP-2U$6a||NogfJCui?WCmsS3)?)i38 z{as@eda7B4qKbP+3w102(1w#45HYW$vBUhW%25p_KTl^U_A^9XkN;2phxfv|vi^#h(pI4?*}ncDuCDpTXffavo^C z0Ewiicc<8y&9nbg$hC(v!M1TGVUpLGVU~>~jLcb#u_eYR%5f2z^AN(zw$G`^nT;v7 zhRWf}%ZRT}PNg(7$J$b)qU2QQAoR|AeSduaeb@cRbKk$~zOVbae%J4>`?{a!BA&Dr z+}3PMpDCMIvTJQT@=OZDm3^$foW8{jiVU2yWlFt)O9LeLO~-wiNspX5Ke=bIu26BF z@dxV;ef8#nlr}Ayomm1^ruiI54sb{Q{Mmj6T zJ4UuOPjEe-jID}gn3JIYNRMQ0{y$GQ&L$H>An@}ZA)!FHQ*?016^O4P#LV2n9|%7a zmJ+h>0^#Q%zD5vZh>-yFh>5u@0QdFcv0o7yBZ&63ydOD0e67t)j82%D8yTX|MhLW> zA<7JmFgA9y7wom598oBf6E=pB|DPjh^xqvABXhIT!0uxtEt+#=xubISqSYdM^B`Lk z*iDRLDy1;LOWvoxA$u^86e@=sG>=BzQhlxL-$@MFb6}xau9KpYtXtVdov~R%4l|vQ zkM^{|r?5etCHv+N#o?~4ogu|I1DY#C(m4AdS)1Sztm}Z!|bShoG z^~pM0`~lL8ufVqJ2e(MC=<P&cy+D*orS0?YXu!~#80R;dp4tDB#I0IzN%=9Knpd8z#J~o zsR0V(T@>XTf-IDszMap~X2rl@ymyxD0mAzj7!Uep-CT1@wq*G)#V7!l2_A%}gPQ3$ z{Mnp(*cc|P_{!7gx0<1ok|m3K%5-YqVaGo|#5y%-1}8zmU*k!H6j4|0d8NX5578sH zL0!^2*7N`k%p*mFTJbaS*fWpd=`vW~963Rg)AJtoerjJRFX|eij*0{w1&un1+P5B^ zm+Hx^he9_rZPq)l`A#~vn#RopD76+;1s9%9^nY4Vw0{~DLVAjpi=mPg+D(F+76(iF z%?RIfEICOkSr&$7MP$NKVY~ql*$pZngWubi)A$b}qU6~7PCyGR^`(%3oS=>+rS>Pp zRzS-!qhk5Oqr(c|Pa!&P#qpA*NDVh_OyC>8Pj{-JybLd->l0(LOTnVhPu1rlt)>A7 z-iP}g^r2AeO+JTgQd81a@=TX4v)mL&)8wUa@Etl;Xo_osEN@f06W|4Cz-zBn8yM{P zrpcEZ`*N0~0V`==wxq4=cpxNY*i`6QP=^HWP}>Muwqi;Ih@TeMN#c1Si#Z)4r+qu* zaNO2VqzWfdB=_7jP{-Tbih&DB75;GSGeEwsNeq|U8h{jjw%sSBd;oPpnRK1(Zg}up z#=sopd`$GftpLxM^s+JY3L#7{tO77`;ovafF(#0Bu8ezPk$1@j> zw%xsQ>GyU!x)XLcMAyh{gVcp^8Q2Z-vmVS3c2Y0%sc&CUY}mXJ7eOiB)*dbjAJ!+` z@%FQ?Y&m;r)D|j5!{zFkRHn{rgtk~hK>Bk=Ty3EqYNdrsxb;<6sI=lF_ zkqE#quCvNW#n@YKe$48^ySl&{!Pg{3h``l}2lQS9yPaA;{Q9Rmoyt`Bs5yZN)bM%j z*Q@AMVVsoY{W}(LfnIA;1L%Z>t!*tRA;;jeHz!GYfXHG4`h(y9W^0k1$F<_6-_R0O zoSB)JxYkGs2-Mt=OcG+SVx3A z$iMAejd|#FoD)BtvA4$Ht=ff!t4>v6=?M?Duq%-609-ujwgGl!>{#2L#-_M!_D%uEI%I@6~j-(L)SF;?G1?D4YlJ`4r zEUP;_nqIef*|zN(b1<^q6Liysft85#XIMxqhcUDy_?DnzBu15B3pg3U_#}S0jxut1 zguHU-v4n|(m9qZM}LRuPWw>(JAf6neJ{>tnJz zOXhgf%s62Zcppq!t5A5DdL1q qq;y0+F#3UoxyQVJe6RkcG54O*O;eW<1xuQm8kz#Nv>e^gz`p@zB~}{% From fb2894799325c16daa9e15c043e146d2422479fb Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Sat, 20 Feb 2016 16:50:42 -0500 Subject: [PATCH 65/83] create MMSP development branch --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 10690fb..fcb7560 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ Mesoscale Microstructure Simulation Project ==== -[![Build Status](https://travis-ci.org/tkphd/mmsp.svg?branch=master)](https://travis-ci.org/tkphd/mmsp) +[![Build Status](https://travis-ci.org/mesoscale/mmsp.svg?branch=develop)](https://travis-ci.org/mesoscale/mmsp) The goal of the Mesoscale Microstructure Simulation Project (MMSP) is to provide a simple, consistent, and extensible programming interface for all grid and mesh based microstructure From 0767c55c0714e8322497f5a757f051c484dfb638 Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Sat, 20 Feb 2016 16:54:15 -0500 Subject: [PATCH 66/83] travis-ci comments, trigger build --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index 3087f62..c122dd3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,7 @@ +# Mesoscale Microstructure Simulation Project +# Travis Continuous Integration build script +# Questions/comments to trevor.keller@gmail.com (Trevor Keller) + language: cpp dist: trusty sudo: required From 0ea219d2905eac4ad321542cc484e9a2fa79021c Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Mon, 22 Feb 2016 11:29:00 -0500 Subject: [PATCH 67/83] correct arithmetic in conditionals, add colors, pretty formatting --- test/build_examples.sh | 83 +++++++++++++++++++++++++----------------- 1 file changed, 49 insertions(+), 34 deletions(-) diff --git a/test/build_examples.sh b/test/build_examples.sh index 922bc1b..1f03d53 100755 --- a/test/build_examples.sh +++ b/test/build_examples.sh @@ -11,7 +11,7 @@ # Questions/comments to trevor.keller@gmail.com (Trevor Keller) # * Canonical examples exclude the following directories: -# differential_equations/elliptic/Poisson -- segmentation faults +# differential_equations/elliptic/Poisson -- failing due to memory errors # beginners_diffusion -- does not match execution pattern # Valid flags are: @@ -22,8 +22,12 @@ # --short execute tests .1x default # --long execute tests 5x longer than default # --extra execute tests 25x longer than default -# --clean delete binary files after test completes -# --purge delete binaries, data, and images after test completes +# --clean delete generated files (binaries, data, imges) after test completes + +# Set output colors +RED='\033[0;31m' +GRN='\033[0;32m' +WHT='\033[0m' # No Color # Initialize timer and completion counters tstart=$(date +%s) @@ -40,7 +44,7 @@ ITERS=1000 INTER=500 CORES=4 COREMAX=$(nproc) -if [[ $CORES > $COREMAX ]] +if [[ $CORES -gt $COREMAX ]] then CORES=$COREMAX fi @@ -52,7 +56,7 @@ examples=$(pwd) echo -n "Building examples in serial and parallel" -while [[ $# > 0 ]] +while [[ $# -gt 0 ]] do key="$1" case $key in @@ -64,11 +68,6 @@ do echo -n ", cleaning up after" CLEAN=true ;; - --purge) - echo -n ", cleaning up after" - CLEAN=true - PURGE=true - ;; --noexec) echo -n ", not executing" NEXEC=true @@ -121,6 +120,8 @@ then fi fi +echo "---------------------------------------------------------------------------" + exdirs=("coarsening/grain_growth/anisotropic/Monte_Carlo/" \ "coarsening/grain_growth/anisotropic/phase_field/" \ "coarsening/grain_growth/anisotropic/sparsePF/" \ @@ -170,27 +171,35 @@ do # Run the example in parallel, for speed. mpirun -np $CORES ./parallel --example 2 test.0000.dat 1>test.log 2>error.log \ && mpirun -np $CORES ./parallel test.0000.dat $ITERS $INTER 1>>test.log 2>>error.log - # Return codes are not reliable. Litter the hard drive with files instead. + # Return codes are not reliable. Save errors to disk for postmortem. if [[ -f error.log ]] && [[ $(wc -w error.log) > 1 ]] then + echo -e "${RED} --FAILED--${WHT}" ((nRunErr++)) - wc -w error.log if [[ -f error.log ]] then - head error.log + echo " error.log has the details (head follows)" + head error.log | sed -e 's/^/ /' fi else ((nParRun++)) rm -f error.log + if [[ ! $NOVIZ ]] + then + # Show the result + for f in *.dat + do + mmsp2png --zoom $f >>test.log + done + fi + exfin=$(date +%s) + exlapse=$(echo "$exfin-$exstart" | bc -l) + printf "${GRN}%3d seconds${WHT}\n" $exlapse fi - if [[ ! $NOVIZ ]] - then - # Show the result - for f in *.dat - do - mmsp2png --zoom $f >>test.log - done - fi + else + exfin=$(date +%s) + exlapse=$(echo "$exfin-$exstart" | bc -l) + printf "${GRN}%3d seconds${WHT}\n" $exlapse fi # Clean up binaries and images if [[ $CLEAN ]] @@ -199,33 +208,39 @@ do rm -f test.*.dat rm -f test.log rm -f error.log - if [[ $PURGE ]] - then - rm -f test.*.png - fi + rm -f test.*.png fi - - exfin=$(date +%s) - exlapse=$(echo "$exfin-$exstart" | bc -l) - printf "%3d seconds\n" $exlapse done cd ${examples} tfinish=$(date +%s) elapsed=$(echo "$tfinish-$tstart" | bc -l) - +echo "---------------------------------------------------------------------------" printf "Elapsed time: %53d seconds\n" $elapsed echo -printf "%2d serial examples compiled successfully, %2d failed.\n" $nSerBld $nSerErr -printf "%2d parallel examples compiled successfully, %2d failed.\n" $nParBld $nParErr -printf "%2d parallel examples executed successfully, %2d failed.\n" $nParRun $nRunErr +printf "%2d serial examples compiled successfully" $nSerBld +if [[ $nSerErr > 0 ]] +then + printf ", %2d failed" $nSerErr +fi +printf "\n%2d parallel examples compiled successfully" $nParBld +if [[ $nParErr > 0 ]] +then + printf ", %2d failed" $nParErr +fi +printf "\n%2d parallel examples executed successfully" $nParRun +if [[ $nRunErr > 0 ]] +then + printf ", %2d failed" $nRunErr +fi +echo cd ../test/ AllERR=$(echo "$nSerErr+$nParErr+$nRunErr" | bc -l) if [[ $AllERR > 0 ]] then - echo "${AllERR} tests failed." echo + echo "Build error(s) detected: ${AllERR} tests failed." exit 1 fi From 49474d2a4e91bb5746b33d41988421da2d763eb8 Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Wed, 24 Feb 2016 18:16:42 -0500 Subject: [PATCH 68/83] rectangular domains for examples without geometric initial condition Square domains can obfuscate errors in visualization utilities. Making the simulation domains rectangular generates clear discontinuities when the utility is producing incorrectly-formatted data. --- .../anisotropic/Monte_Carlo/graingrowth.cpp | 7 +- .../anisotropic/phase_field/graingrowth.cpp | 3 +- .../isotropic/Monte_Carlo/graingrowth.cpp | 7 +- .../isotropic/phase_field/ostwald.cpp | 6 +- .../anisotropic/Monte_Carlo/zener.cpp | 5 +- .../anisotropic/phase_field/zener.cpp | 5 +- .../anisotropic/sparsePF/zener.cpp | 5 +- .../isotropic/Monte_Carlo/zener.cpp | 5 +- .../isotropic/phase_field/zener.cpp | 2 +- .../allen-cahn/allen-cahn.cpp | 6 +- .../convex_splitting/cahn-hilliard.cpp | 2 +- .../cahn-hilliard/explicit/cahn-hilliard.cpp | 6 +- .../phase_transitions/model_A/model_A.cpp | 6 +- .../phase_transitions/model_B/model_B.cpp | 6 +- .../phase_transitions/spinodal/spinodal.cpp | 6 +- .../Heisenberg/heisenberg.cpp | 6 +- .../statistical_mechanics/Ising/ising.cpp | 6 +- .../statistical_mechanics/Potts/potts.cpp | 6 +- test/build_examples.sh | 80 +++++++++++++------ 19 files changed, 115 insertions(+), 60 deletions(-) diff --git a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp index 55e65c3..70f5c74 100644 --- a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp +++ b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp @@ -29,15 +29,16 @@ void generate(int dim, const char* filename) } if (dim == 2) { - int L=64; - GRID2D initGrid(0, 0, L, 0, L); + int L=128; + GRID2D initGrid(0, 0, 2*L, 0, L); for (int i = 0; i < nodes(initGrid); i++) initGrid(i) = rand() % 20; output(initGrid, filename); } else if (dim == 3) { - GRID3D initGrid(0, 0, 32, 0, 32, 0, 32); + int L=64; + GRID3D initGrid(0, 0, 2*L, 0, L, 0, L); for (int i = 0; i < nodes(initGrid); i++) initGrid(i) = rand() % 20; diff --git a/examples/coarsening/grain_growth/anisotropic/phase_field/graingrowth.cpp b/examples/coarsening/grain_growth/anisotropic/phase_field/graingrowth.cpp index 392d43c..d8f46dc 100644 --- a/examples/coarsening/grain_growth/anisotropic/phase_field/graingrowth.cpp +++ b/examples/coarsening/grain_growth/anisotropic/phase_field/graingrowth.cpp @@ -58,7 +58,8 @@ void generate(int dim, const char* filename) } if (dim==3) { - GRID3D initGrid(4,0,128,0,128); + int L=64; + GRID3D initGrid(4,0,L,0,L,0,L); for (int i=0; i x = position(initGrid,i); diff --git a/examples/coarsening/zener_pinning/anisotropic/phase_field/zener.cpp b/examples/coarsening/zener_pinning/anisotropic/phase_field/zener.cpp index f881268..c98033f 100644 --- a/examples/coarsening/zener_pinning/anisotropic/phase_field/zener.cpp +++ b/examples/coarsening/zener_pinning/anisotropic/phase_field/zener.cpp @@ -41,7 +41,7 @@ void generate(int dim, const char* filename) } if (dim==2) { - int L=256; + int L=128; GRID2D initGrid(3,0,L,0,L); for (int i=0; i x = position(initGrid,i); diff --git a/examples/coarsening/zener_pinning/anisotropic/sparsePF/zener.cpp b/examples/coarsening/zener_pinning/anisotropic/sparsePF/zener.cpp index 52dc175..4532349 100644 --- a/examples/coarsening/zener_pinning/anisotropic/sparsePF/zener.cpp +++ b/examples/coarsening/zener_pinning/anisotropic/sparsePF/zener.cpp @@ -39,7 +39,7 @@ void generate(int dim, const char* filename) } if (dim==2) { - int L=256; + int L=128; GRID2D initGrid(0,0,L,0,L); for (int i=0; i x = position(initGrid,i); diff --git a/examples/coarsening/zener_pinning/isotropic/Monte_Carlo/zener.cpp b/examples/coarsening/zener_pinning/isotropic/Monte_Carlo/zener.cpp index 5c180a8..76aa835 100644 --- a/examples/coarsening/zener_pinning/isotropic/Monte_Carlo/zener.cpp +++ b/examples/coarsening/zener_pinning/isotropic/Monte_Carlo/zener.cpp @@ -40,7 +40,7 @@ void generate(int dim, const char* filename) } if (dim==2) { - int L=256; + int L=128; GRID2D grid(0,0,L,0,L); for (int i=0; i x = position(grid,i); diff --git a/examples/coarsening/zener_pinning/isotropic/phase_field/zener.cpp b/examples/coarsening/zener_pinning/isotropic/phase_field/zener.cpp index 3d020da..553bbb6 100644 --- a/examples/coarsening/zener_pinning/isotropic/phase_field/zener.cpp +++ b/examples/coarsening/zener_pinning/isotropic/phase_field/zener.cpp @@ -40,7 +40,7 @@ void generate(int dim, const char* filename) } if (dim==2) { - int L=256; + int L=128; GRID2D initGrid(3,0,L,0,L); for (int i=0; i > initGrid(2,0,edge,0,edge); // field 0 is c, field 1 is mu + grid<2,vector > initGrid(2,0,2*edge,0,edge); // field 0 is c, field 1 is mu for (int d=0; dtest.log 2>error.log \ + mpirun -np $CORES ./parallel --example $DIM test.0000.dat 1>test.log 2>error.log \ && mpirun -np $CORES ./parallel test.0000.dat $ITERS $INTER 1>>test.log 2>>error.log # Return codes are not reliable. Save errors to disk for postmortem. if [[ -f error.log ]] && [[ $(wc -w error.log) > 1 ]] @@ -186,11 +212,16 @@ do rm -f error.log if [[ ! $NOVIZ ]] then - # Show the result - for f in *.dat - do - mmsp2png --zoom $f >>test.log - done + if [[ ! $PVD ]] + then + # Show the result + for f in *.dat + do + mmsp2png --zoom $f >>test.log + done + else + mmsp2pvd --output=test.pvd test.*.dat >>test.log + fi fi exfin=$(date +%s) exlapse=$(echo "$exfin-$exstart" | bc -l) @@ -206,9 +237,8 @@ do then make -s clean rm -f test.*.dat - rm -f test.log - rm -f error.log - rm -f test.*.png + rm -f test.log error.log + rm -f test.*.png test.pvd test.*.vti fi done From 4dc5904b4a27e1c78a13e7d7dd3bd90bf57cdc10 Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Wed, 24 Feb 2016 20:45:57 -0500 Subject: [PATCH 69/83] restore Poisson example, serial execution only --- .../elliptic/Poisson/multigrid.hpp | 2 +- .../elliptic/Poisson/poisson.cpp | 6 +- include/MMSP.main.hpp | 2 + test/build_examples.sh | 56 +++++++++++++------ 4 files changed, 45 insertions(+), 21 deletions(-) diff --git a/examples/differential_equations/elliptic/Poisson/multigrid.hpp b/examples/differential_equations/elliptic/Poisson/multigrid.hpp index 36851e4..316844a 100644 --- a/examples/differential_equations/elliptic/Poisson/multigrid.hpp +++ b/examples/differential_equations/elliptic/Poisson/multigrid.hpp @@ -194,7 +194,7 @@ void MG(grid& u, const grid& f, int stride, int gamma=1, int nu1=2 smooth(u,f,s,nu2); } -template +template void FMG(grid& u, const grid& f, int gamma=1, int nu1=2, int nu2=2) { // solve at coarsest level diff --git a/examples/differential_equations/elliptic/Poisson/poisson.cpp b/examples/differential_equations/elliptic/Poisson/poisson.cpp index fee8f9a..b0e69b0 100644 --- a/examples/differential_equations/elliptic/Poisson/poisson.cpp +++ b/examples/differential_equations/elliptic/Poisson/poisson.cpp @@ -191,7 +191,7 @@ void defect(const grid<3,T>& u, const grid<3,T>& f, grid<3,T>& d, int stride) void generate(int dim, const char* filename) { if (dim==1) { - grid<1,double> initGrid(1,0,128); + grid<1,double> initGrid(1,0,257); for (int n=0; n x = position(initGrid, n); @@ -203,7 +203,7 @@ void generate(int dim, const char* filename) } if (dim==2) { - int L=256; + int L=129; grid<2,double> initGrid(1,0,L,0,L); for (int n=0; n initGrid(1,0,L,0,L,0,L); for (int n=0; n 0 ]] then - ((nParBld++)) - else - ((nParErr++)) + if make $MFLAG parallel + then + ((nParBld++)) + else + ((nParErr++)) + fi fi if [[ -f parallel ]] && [[ ! $NEXEC ]] then + RUNCMD="mpirun -np $CORES ./parallel" + if [[ ${exdirs[$i]} == *"Poisson"* ]] + then + RUNCMD="./poisson" + fi # Run the example in parallel, for speed. - mpirun -np $CORES ./parallel --example $DIM test.0000.dat 1>test.log 2>error.log \ - && mpirun -np $CORES ./parallel test.0000.dat $ITERS $INTER 1>>test.log 2>>error.log + $RUNCMD --example $DIM test.0000.dat 1>test.log 2>error.log \ + && $RUNCMD test.0000.dat $ITERS $INTER 1>>test.log 2>>error.log # Return codes are not reliable. Save errors to disk for postmortem. if [[ -f error.log ]] && [[ $(wc -w error.log) > 1 ]] then @@ -227,6 +239,16 @@ do exlapse=$(echo "$exfin-$exstart" | bc -l) printf "${GRN}%3d seconds${WHT}\n" $exlapse fi + elif [[ ${exdirs[$i]} == *"beginners"* ]] + then + exfin=$(date +%s) + exlapse=$(echo "$exfin-$exstart" | bc -l) + if [[ ! $NEXEC ]] + then + printf "${YLW}%3d seconds\n example does not match generic pattern for execution${WHT}\n" $exlapse + else + printf "${GRN}%3d seconds${WHT}\n" $exlapse + fi else exfin=$(date +%s) exlapse=$(echo "$exfin-$exstart" | bc -l) @@ -249,17 +271,17 @@ elapsed=$(echo "$tfinish-$tstart" | bc -l) echo "---------------------------------------------------------------------------" printf "Elapsed time: %53d seconds\n" $elapsed echo -printf "%2d serial examples compiled successfully" $nSerBld +printf "%2d examples compiled successfully in serial " $nSerBld if [[ $nSerErr > 0 ]] then printf ", %2d failed" $nSerErr fi -printf "\n%2d parallel examples compiled successfully" $nParBld +printf "\n%2d examples compiled successfully in parallel" $nParBld if [[ $nParErr > 0 ]] then printf ", %2d failed" $nParErr fi -printf "\n%2d parallel examples executed successfully" $nParRun +printf "\n%2d examples executed successfully " $nParRun if [[ $nRunErr > 0 ]] then printf ", %2d failed" $nRunErr From 4514c52468c78570c9478f6cd337e8706db7b07e Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Thu, 25 Feb 2016 13:05:29 -0500 Subject: [PATCH 70/83] correct return values in grid arithmetic operators --- include/MMSP.grid.hpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/MMSP.grid.hpp b/include/MMSP.grid.hpp index 721fdeb..d1843da 100644 --- a/include/MMSP.grid.hpp +++ b/include/MMSP.grid.hpp @@ -509,24 +509,30 @@ class grid { for (int i=0; i(value); + return *this; } template grid& operator=(const grid& GRID) { + if (this == &GRID) + return *this; for (int i=0; i(GRID.data[i]); + return *this; } template grid& operator+=(const grid& GRID) { for (int i=0; i(GRID.data[i]); + return *this; } template grid& operator-=(const grid& GRID) { for (int i=0; i(GRID.data[i]); + return *this; } From 04b0a3b90b5082cc70cc905c2e436c6ebf646397 Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Thu, 25 Feb 2016 13:07:02 -0500 Subject: [PATCH 71/83] clear Poisson -Wall warnings --- .../elliptic/Poisson/Makefile | 2 +- .../elliptic/Poisson/multigrid.hpp | 6 ++-- .../elliptic/Poisson/poisson.cpp | 36 +++++++++---------- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/examples/differential_equations/elliptic/Poisson/Makefile b/examples/differential_equations/elliptic/Poisson/Makefile index de4e740..a509277 100644 --- a/examples/differential_equations/elliptic/Poisson/Makefile +++ b/examples/differential_equations/elliptic/Poisson/Makefile @@ -7,7 +7,7 @@ incdir = ../../../../include # compilers/flags compiler = g++ pcompiler = mpic++ -flags = -O3 -I $(incdir) +flags = -O3 -Wall -I $(incdir) pflags = $(flags) -include mpi.h # the program diff --git a/examples/differential_equations/elliptic/Poisson/multigrid.hpp b/examples/differential_equations/elliptic/Poisson/multigrid.hpp index 316844a..4dbd907 100644 --- a/examples/differential_equations/elliptic/Poisson/multigrid.hpp +++ b/examples/differential_equations/elliptic/Poisson/multigrid.hpp @@ -152,13 +152,13 @@ template void refine(grid<3,T>& u, int stride, std::string method=" } } -template +template void MG(grid& u, const grid& f, int stride, int gamma=1, int nu1=2, int nu2=2) { - // standard multigrid cycle + // standard multigrid cycle int s = stride; - // solve if at coarsest level + // solve at coarsest level bool solve = false; for (int i=0; i void smooth(grid<1,T>& u, const grid<1,T>& f, int stride, int iterations=1) { // red-black Gauss-Seidel iteration - int s = stride; + int s = stride; + int x1 = MMSP::x1(u); double dx = s*MMSP::dx(u); - double x1 = MMSP::x1(u); double dx2 = dx*dx; double w = 1.0/(2.0/dx2); double wx = w/dx2; @@ -36,11 +36,11 @@ template void smooth(grid<2,T>& u, const grid<2,T>& f, int stride, int iterations=1) { // red-black Gauss-Seidel iteration - int s = stride; + int s = stride; + int x1 = MMSP::x1(u); + int y1 = MMSP::y1(u); double dx = s*MMSP::dx(u); double dy = s*MMSP::dy(u); - double x1 = MMSP::x1(u); - double y1 = MMSP::y1(u); double dx2 = dx*dx; double dy2 = dy*dy; double w = 1.0/(2.0/dx2+2.0/dy2); @@ -65,13 +65,13 @@ template void smooth(grid<3,T>& u, const grid<3,T>& f, int stride, int iterations=1) { // red-black Gauss-Seidel iteration - int s = stride; + int s = stride; + int x1 = MMSP::x1(u); + int y1 = MMSP::y1(u); + int z1 = MMSP::z1(u); double dx = s*MMSP::dx(u); double dy = s*MMSP::dy(u); double dz = s*MMSP::dz(u); - double x1 = MMSP::x1(u); - double y1 = MMSP::y1(u); - double z1 = MMSP::z1(u); double dx2 = dx*dx; double dy2 = dy*dy; double dz2 = dz*dz; @@ -102,9 +102,9 @@ template void defect(const grid<1,T>& u, const grid<1,T>& f, grid<1,T>& d, int stride) { // compute defect for Poisson equation lap(u) = f - int s = stride; + int s = stride; + int x1 = MMSP::x1(u); double dx = s*MMSP::dx(u); - double x1 = MMSP::x1(u); double dx2 = dx*dx; double wx = 1.0/dx2; @@ -121,11 +121,11 @@ template void defect(const grid<2,T>& u, const grid<2,T>& f, grid<2,T>& d, int stride) { // compute defect for Poisson equation lap(u) = f - int s = stride; + int s = stride; + int x1 = MMSP::x1(u); + int y1 = MMSP::y1(u); double dx = s*MMSP::dx(u); double dy = s*MMSP::dy(u); - double x1 = MMSP::x1(u); - double y1 = MMSP::y1(u); double dx2 = dx*dx; double dy2 = dy*dy; double wx = 1.0/dx2; @@ -150,13 +150,13 @@ template void defect(const grid<3,T>& u, const grid<3,T>& f, grid<3,T>& d, int stride) { // compute defect for Poisson equation lap(u) = f - int s = stride; + int s = stride; + int x1 = MMSP::x1(u); + int y1 = MMSP::y1(u); + int z1 = MMSP::z1(u); double dx = s*MMSP::dx(u); double dy = s*MMSP::dy(u); double dz = s*MMSP::dz(u); - double x1 = MMSP::x1(u); - double y1 = MMSP::y1(u); - double z1 = MMSP::z1(u); double dx2 = dx*dx; double dy2 = dy*dy; double dz2 = dz*dz; From 8f82fc82741fe77cc848610a39849d60e6c4a507 Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Thu, 25 Feb 2016 13:08:27 -0500 Subject: [PATCH 72/83] remove debug builds, generate PVD alongside PNG (not either or) --- .../anisotropic/Monte_Carlo/Makefile | 6 +-- .../cahn-hilliard/convex_splitting/Makefile | 7 +-- test/build_examples.sh | 53 +++++++++++-------- 3 files changed, 33 insertions(+), 33 deletions(-) diff --git a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/Makefile b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/Makefile index e75203d..7483eed 100644 --- a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/Makefile +++ b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/Makefile @@ -9,18 +9,14 @@ incdir = ../../../../../include compiler = g++ pcompiler = mpic++ flags = -O3 -I $(incdir) -gflags = -g -I $(incdir) pflags = $(flags) -include mpi.h # the program graingrowth: graingrowth.cpp $(compiler) $(flags) $< -o $@ -lz -debug: graingrowth.cpp - $(compiler) $(gflags) $< -o $@ -lz - parallel: graingrowth.cpp $(pcompiler) $(pflags) $< -o $@ -lz clean: - rm -f graingrowth debug parallel + rm -f graingrowth parallel diff --git a/examples/phase_transitions/cahn-hilliard/convex_splitting/Makefile b/examples/phase_transitions/cahn-hilliard/convex_splitting/Makefile index c746601..b553b94 100644 --- a/examples/phase_transitions/cahn-hilliard/convex_splitting/Makefile +++ b/examples/phase_transitions/cahn-hilliard/convex_splitting/Makefile @@ -10,7 +10,6 @@ incdir = ../../../../include compiler = g++ pcompiler = mpic++ flags = -O3 -DVANILLA -I $(incdir) -gflags = -pg -O2 -DVANILLA -Wall -I $(incdir) pflags = $(flags) -include mpi.h # the program @@ -20,9 +19,5 @@ cahn-hilliard: cahn-hilliard.cpp parallel: cahn-hilliard.cpp $(pcompiler) $(pflags) $< -o $@ -lz -# for profiling -debug: cahn-hilliard.cpp - $(compiler) $(gflags) $< -o $@ -lz - clean: - rm -f cahn-hilliard parallel debug + rm -f cahn-hilliard parallel diff --git a/test/build_examples.sh b/test/build_examples.sh index b6d7cd1..8f66df1 100755 --- a/test/build_examples.sh +++ b/test/build_examples.sh @@ -30,11 +30,13 @@ # --clean delete generated files (binaries, data, imges) after test completes # Set output colors -RED='\033[0;31m' # red -GRN='\033[0;32m' # green -YLW='\033[0;33m' # yellow +RED='\033[0;31m' # red +BRED='\033[1;31m' # bold red +GRN='\033[0;32m' # green +BGRN='\033[1;32m' # bold green +YLW='\033[0;33m' # yellow BYLW='\033[1;33m' # bold yellow -WHT='\033[0m' # normal +WHT='\033[0m' # normal # Initialize timer and completion counters tstart=$(date +%s) @@ -46,14 +48,17 @@ nParBld=0 nParRun=0 MFLAG="-s" -# Set execution parameters -DIM=2 -ITERS=1000 -INTER=500 -CORES=4 -COREMAX=$(nproc) +# Default execution parameters +DIM=2 # grid dimensions +ITERS=1000 # number of steps +INTER=500 # steps between checkpoint +ZED="0000" # checkpoint filename format +CORES=4 # MPI ranks to use +COREMAX=$(nproc) # MPI ranks available if [[ $CORES -gt $COREMAX ]] then + # A lot of machines, Travis-CI build bots included, + # have only 2 cores available. Let's be polite. CORES=$COREMAX fi @@ -63,7 +68,6 @@ examples=$(pwd) echo -n "Building examples in serial and parallel" - while [[ $# -gt 0 ]] do key="$1" @@ -96,6 +100,7 @@ do --short) ITERS=$(($ITERS/10)) INTER=100 + ZED="000" ;; --long) ITERS=$((5*$ITERS)) @@ -104,6 +109,7 @@ do --extra) ITERS=$((25*$ITERS)) INTER=5000 + ZED="00000" ;; --noviz) echo -n ", no PNG output" @@ -190,7 +196,7 @@ do else ((nSerErr++)) fi - if [[ $i > 0 ]] + if [[ ${exdirs[$i]} != *"beginners"* ]] then if make $MFLAG parallel then @@ -201,14 +207,15 @@ do fi if [[ -f parallel ]] && [[ ! $NEXEC ]] then + rm -f test.*.dat test.*.png test.*.vti test.pvd RUNCMD="mpirun -np $CORES ./parallel" if [[ ${exdirs[$i]} == *"Poisson"* ]] then RUNCMD="./poisson" fi # Run the example in parallel, for speed. - $RUNCMD --example $DIM test.0000.dat 1>test.log 2>error.log \ - && $RUNCMD test.0000.dat $ITERS $INTER 1>>test.log 2>>error.log + $RUNCMD --example $DIM test.$ZED.dat 1>test.log 2>error.log \ + && $RUNCMD test.$ZED.dat $ITERS $INTER 1>>test.log 2>>error.log # Return codes are not reliable. Save errors to disk for postmortem. if [[ -f error.log ]] && [[ $(wc -w error.log) > 1 ]] then @@ -224,14 +231,13 @@ do rm -f error.log if [[ ! $NOVIZ ]] then - if [[ ! $PVD ]] + # Show the result + for f in *.dat + do + mmsp2png --zoom $f >>test.log + done + if [[ $PVD ]] then - # Show the result - for f in *.dat - do - mmsp2png --zoom $f >>test.log - done - else mmsp2pvd --output=test.pvd test.*.dat >>test.log fi fi @@ -293,6 +299,9 @@ AllERR=$(echo "$nSerErr+$nParErr+$nRunErr" | bc -l) if [[ $AllERR > 0 ]] then echo - echo "Build error(s) detected: ${AllERR} tests failed." + echo -e "${BRED}Build error(s) detected: ${AllERR} tests failed.${WHT}" exit 1 fi + +echo +echo -e "${BGRN}Build tests successfully completed.${WHT}" From 569556fc5795658e6efdeff246498a4479b7d883 Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Thu, 25 Feb 2016 14:06:24 -0500 Subject: [PATCH 73/83] small 1D grids crash I/O --- examples/phase_transitions/allen-cahn/allen-cahn.cpp | 3 ++- .../phase_transitions/cahn-hilliard/explicit/cahn-hilliard.cpp | 3 ++- examples/phase_transitions/model_A/model_A.cpp | 3 ++- examples/phase_transitions/model_B/model_B.cpp | 3 ++- examples/phase_transitions/spinodal/spinodal.cpp | 3 ++- 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/examples/phase_transitions/allen-cahn/allen-cahn.cpp b/examples/phase_transitions/allen-cahn/allen-cahn.cpp index 9f58a49..832136a 100644 --- a/examples/phase_transitions/allen-cahn/allen-cahn.cpp +++ b/examples/phase_transitions/allen-cahn/allen-cahn.cpp @@ -14,7 +14,8 @@ void generate(int dim, const char* filename) { // srand() is called exactly once in MMSP.main.hpp. Do not call it here. if (dim==1) { - GRID1D initGrid(0,0,128); + int L=1024; + GRID1D initGrid(0,0,L); for (int i=0; i Date: Thu, 25 Feb 2016 19:33:51 -0500 Subject: [PATCH 74/83] grid size adjustments Update convex-splitting example for 1D and 3D execution, parametrize grid sizes in other examples, issue warning for large 3D build tests --- .../anisotropic/Monte_Carlo/graingrowth.cpp | 7 +- .../anisotropic/phase_field/graingrowth.cpp | 21 +-- .../anisotropic/sparsePF/graingrowth.cpp | 22 +-- .../isotropic/Monte_Carlo/graingrowth.cpp | 7 +- .../isotropic/phase_field/graingrowth.cpp | 14 +- .../isotropic/sparsePF/graingrowth.cpp | 14 +- .../isotropic/phase_field/ostwald.cpp | 3 +- .../anisotropic/Monte_Carlo/zener.cpp | 20 ++- .../anisotropic/phase_field/zener.cpp | 22 +-- .../anisotropic/sparsePF/zener.cpp | 20 ++- .../isotropic/Monte_Carlo/zener.cpp | 82 +++++----- .../isotropic/phase_field/zener.cpp | 23 +-- .../isotropic/sparsePF/zener.cpp | 23 +-- .../convex_splitting/cahn-hilliard.cpp | 149 +++++++++--------- .../Heisenberg/heisenberg.cpp | 5 +- .../statistical_mechanics/Ising/ising.cpp | 5 +- .../statistical_mechanics/Potts/potts.cpp | 5 +- test/build_examples.sh | 6 +- 18 files changed, 245 insertions(+), 203 deletions(-) diff --git a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp index 70f5c74..d4cc0e6 100644 --- a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp +++ b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp @@ -20,7 +20,8 @@ void generate(int dim, const char* filename) { // srand() is called exactly once in MMSP.main.hpp. Do not call it here. if (dim == 1) { - GRID1D initGrid(0, 0, 128); + int L=1024; + GRID1D initGrid(0, 0, L); for (int i = 0; i < nodes(initGrid); i++) initGrid(i) = rand() % 100; @@ -37,8 +38,8 @@ void generate(int dim, const char* filename) output(initGrid, filename); } else if (dim == 3) { - int L=64; - GRID3D initGrid(0, 0, 2*L, 0, L, 0, L); + int L=32; + GRID3D initGrid(0, 0, 2*L, 0, L, 0, L/2); for (int i = 0; i < nodes(initGrid); i++) initGrid(i) = rand() % 20; diff --git a/examples/coarsening/grain_growth/anisotropic/phase_field/graingrowth.cpp b/examples/coarsening/grain_growth/anisotropic/phase_field/graingrowth.cpp index d8f46dc..66ef7c0 100644 --- a/examples/coarsening/grain_growth/anisotropic/phase_field/graingrowth.cpp +++ b/examples/coarsening/grain_growth/anisotropic/phase_field/graingrowth.cpp @@ -14,7 +14,8 @@ namespace MMSP{ void generate(int dim, const char* filename) { if (dim==1) { - GRID1D initGrid(4,0,128); + int L=1024; + GRID1D initGrid(4,0,L); for (int i=0; i x = position(initGrid,i); - if (x[0]<32) initGrid(i)[3] = 1.0; - else if (x[0]>96) initGrid(i)[3] = 1.0; - else initGrid(i)[0] = 1.0; + if (x[0]3*L/4) initGrid(i)[3] = 1.0; + else initGrid(i)[0] = 1.0; } output(initGrid,filename); @@ -58,7 +59,7 @@ void generate(int dim, const char* filename) } if (dim==3) { - int L=64; + int L=32; GRID3D initGrid(4,0,L,0,L,0,L); for (int i=0; i x = position(initGrid,i); - if (x[0]<16) { - if (x[1]<32) initGrid(i)[2] = 1.0; + if (x[0]48) { - if (x[1]<32) initGrid(i)[2] = 1.0; + else if (x[0]>3*L/4) { + if (x[1]48) initGrid(i)[1] = 1.0; + if (x[1]3*L/4) initGrid(i)[1] = 1.0; else initGrid(i)[0] = 1.0; } } diff --git a/examples/coarsening/grain_growth/anisotropic/sparsePF/graingrowth.cpp b/examples/coarsening/grain_growth/anisotropic/sparsePF/graingrowth.cpp index ecf4f73..52231b7 100644 --- a/examples/coarsening/grain_growth/anisotropic/sparsePF/graingrowth.cpp +++ b/examples/coarsening/grain_growth/anisotropic/sparsePF/graingrowth.cpp @@ -14,14 +14,15 @@ namespace MMSP{ void generate(int dim, const char* filename) { if (dim==1) { - GRID1D initGrid(0,0,128); + int L=1024; + GRID1D initGrid(0,0,L); for (int i=0; i x = position(initGrid,i); - if (x[0]<32) set(initGrid(i),3) = 1.0; - else if (x[0]>96) set(initGrid(i),3) = 1.0; - else set(initGrid(i),0) = 1.0; + if (x[0]3*L/4) set(initGrid(i),3) = 1.0; + else set(initGrid(i),0) = 1.0; } output(initGrid,filename); @@ -52,21 +53,22 @@ void generate(int dim, const char* filename) } if (dim==3) { - GRID3D initGrid(0,0,64,0,64,0,64); + int L=32; + GRID3D initGrid(0,0,L,0,L,0,L); for (int i=0; i x = position(initGrid,i); - if (x[0]<16) { - if (x[1]<32) set(initGrid(i),2) = 1.0; + if (x[0]48) { - if (x[1]<32) set(initGrid(i),2) = 1.0; + else if (x[0]>3*L/4) { + if (x[1]48) set(initGrid(i),1) = 1.0; + if (x[1]3*L/4) set(initGrid(i),1) = 1.0; else set(initGrid(i),0) = 1.0; } } diff --git a/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp b/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp index a758e6f..311f382 100644 --- a/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp +++ b/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp @@ -19,7 +19,8 @@ void generate(int dim, const char* filename) { // srand() is called exactly once in MMSP.main.hpp. Do not call it here. if (dim == 1) { - GRID1D initGrid(0, 0, 128); + int L=1024; + GRID1D initGrid(0, 0, L); for (int i = 0; i < nodes(initGrid); i++) initGrid(i) = rand() % 100; @@ -36,8 +37,8 @@ void generate(int dim, const char* filename) output(initGrid, filename); } else if (dim == 3) { - int L=64; - GRID3D initGrid(0, 0, 2*L, 0, L, 0, L); + int L=32; + GRID3D initGrid(0, 0, 2*L, 0, L, 0, L/2); for (int i = 0; i < nodes(initGrid); i++) initGrid(i) = rand() % 20; diff --git a/examples/coarsening/grain_growth/isotropic/phase_field/graingrowth.cpp b/examples/coarsening/grain_growth/isotropic/phase_field/graingrowth.cpp index 1a66ac1..dfbf70e 100644 --- a/examples/coarsening/grain_growth/isotropic/phase_field/graingrowth.cpp +++ b/examples/coarsening/grain_growth/isotropic/phase_field/graingrowth.cpp @@ -13,12 +13,13 @@ namespace MMSP{ void generate(int dim, const char* filename) { if (dim==1) { - GRID1D initGrid(2,0,128); + int L=1024; + GRID1D initGrid(2,0,L); for (int i=0; i x = position(initGrid,i); - double d = 64.0-x[0]; - if (d<32.0) { + double d = L/2-x[0]; + if (d x = position(initGrid,i); - double d = sqrt(pow(32.0-x[0],2)+pow(32.0-x[1],2)+pow(32.0-x[2],2)); - if (d<16.0) { + double d = sqrt(pow(L/2-x[0],2)+pow(L/2-x[1],2)+pow(L/2-x[2],2)); + if (d x = position(initGrid,i); - double d = 64.0-x[0]; - if (d<32.0) set(initGrid(i),1)= 1.0; + double d = L/2-x[0]; + if (d x = position(initGrid,i); - double d = sqrt(pow(32.0-x[0],2)+pow(32.0-x[1],2)+pow(32.0-x[2],2)); - if (d<16.0) set(initGrid(i),1)= 1.0; + double d = sqrt(pow(L/2-x[0],2)+pow(L/2-x[1],2)+pow(L/2-x[2],2)); + if (d x = position(initGrid,i); - double d = 64.0-x[0]; - if (d<32.0) initGrid(i) = 2; + double d = L/2-x[0]; + if (d x = position(initGrid,i); vector p(x); @@ -46,7 +48,8 @@ void generate(int dim, const char* filename) else initGrid(i) = 1; } - for (int j=0; j<50; j++) { + int nParticles=std::max(50,(50*8192)/nodes(initGrid)); + for (int j=0; j x = position(initGrid,i); vector p(x); @@ -64,12 +67,13 @@ void generate(int dim, const char* filename) for (int i=0; i x = position(initGrid,i); - double d = sqrt(pow(32.0-x[0],2)+pow(32.0-x[1],2)+pow(32.0-x[2],2)); - if (d<16.0) initGrid(i) = 2; + double d = sqrt(pow(L/2-x[0],2)+pow(L/2-x[1],2)+pow(L/2-x[2],2)); + if (d x = position(initGrid,i); vector p(x); diff --git a/examples/coarsening/zener_pinning/anisotropic/phase_field/zener.cpp b/examples/coarsening/zener_pinning/anisotropic/phase_field/zener.cpp index c98033f..951c28e 100644 --- a/examples/coarsening/zener_pinning/anisotropic/phase_field/zener.cpp +++ b/examples/coarsening/zener_pinning/anisotropic/phase_field/zener.cpp @@ -15,18 +15,20 @@ void generate(int dim, const char* filename) { // srand() is called exactly once in MMSP.main.hpp. Do not call it here. if (dim==1) { - GRID1D initGrid(3,0,128); + int L=1024; + GRID1D initGrid(3,0,L); for (int i=0; i x = position(initGrid,i); initGrid(i)[0] = 0.0; initGrid(i)[1] = 0.0; - double d = 64.0-x[0]; - if (d<32.0) initGrid(i)[2] = 1.0; + double d = L/2-x[0]; + if (d x = position(initGrid,i); vector p(x); @@ -48,12 +50,13 @@ void generate(int dim, const char* filename) vector x = position(initGrid,i); initGrid(i)[0] = 0.0; initGrid(i)[1] = 0.0; - double d = sqrt(pow(L/2.0-x[0],2)+pow(L/2.0-x[1],2)); + double d = sqrt(pow(L/2-x[0],2)+pow(L/2-x[1],2)); if (d x = position(initGrid,i); vector p(x); @@ -76,12 +79,13 @@ void generate(int dim, const char* filename) vector x = position(initGrid,i); initGrid(i)[0] = 0.0; initGrid(i)[1] = 0.0; - double d = sqrt(pow(32.0-x[0],2)+pow(32.0-x[1],2)+pow(32.0-x[2],2)); - if (d<16.0) initGrid(i)[2] = 1.0; + double d = sqrt(pow(L/2-x[0],2)+pow(L/2-x[1],2)+pow(L/2-x[2],2)); + if (d x = position(initGrid,i); vector p(x); diff --git a/examples/coarsening/zener_pinning/anisotropic/sparsePF/zener.cpp b/examples/coarsening/zener_pinning/anisotropic/sparsePF/zener.cpp index 4532349..91deed4 100644 --- a/examples/coarsening/zener_pinning/anisotropic/sparsePF/zener.cpp +++ b/examples/coarsening/zener_pinning/anisotropic/sparsePF/zener.cpp @@ -15,16 +15,18 @@ void generate(int dim, const char* filename) { // srand() is called exactly once in MMSP.main.hpp. Do not call it here. if (dim==1) { - GRID1D initGrid(0,0,128); + int L=1024; + GRID1D initGrid(0,0,L); for (int i=0; i x = position(initGrid,i); - double d = 64.0-x[0]; - if (d<32.0) set(initGrid(i),2) = 1.0; + double d = L/2-x[0]; + if (d x = position(initGrid,i); vector p(x); @@ -49,7 +51,8 @@ void generate(int dim, const char* filename) else set(initGrid(i),1) = 1.0; } - for (int j=0; j<50; j++) { + int nParticles=std::max(50,(50*8192)/nodes(initGrid)); + for (int j=0; j x = position(initGrid,i); vector p(x); @@ -70,12 +73,13 @@ void generate(int dim, const char* filename) for (int i=0; i x = position(initGrid,i); - double d = sqrt(pow(32.0-x[0],2)+pow(32.0-x[1],2)+pow(32.0-x[2],2)); - if (d<16.0) set(initGrid(i),2) = 1.0; + double d = sqrt(pow(L/2-x[0],2)+pow(L/2-x[1],2)+pow(L/2-x[2],2)); + if (d x = position(initGrid,i); vector p(x); diff --git a/examples/coarsening/zener_pinning/isotropic/Monte_Carlo/zener.cpp b/examples/coarsening/zener_pinning/isotropic/Monte_Carlo/zener.cpp index 76aa835..69349af 100644 --- a/examples/coarsening/zener_pinning/isotropic/Monte_Carlo/zener.cpp +++ b/examples/coarsening/zener_pinning/isotropic/Monte_Carlo/zener.cpp @@ -14,83 +14,87 @@ void generate(int dim, const char* filename) { // srand() is called exactly once in MMSP.main.hpp. Do not call it here. if (dim==1) { - GRID1D grid(0,0,128); - - for (int i=0; i x = position(grid,i); - double d = 64.0-x[0]; - if (d<32.0) grid(i) = 2; - else grid(i) = 1; + int L=1024; + GRID1D initGrid(0,0,L); + + for (int i=0; i x = position(initGrid,i); + double d = L/2-x[0]; + if (d x = position(grid,i); - grid(x) = 0; + int nParticles=std::max(50,(50*8192)/nodes(initGrid)); + for (int j=0; j x = position(initGrid,i); + initGrid(x) = 0; for (int d=0; d x = position(grid,i); + for (int i=0; i x = position(initGrid,i); double d = sqrt(pow(L/2-x[0],2)+pow(L/2-x[1],2)); - if (d x = position(grid,i); - grid(x) = 0; + int nParticles=std::max(50,(50*8192)/nodes(initGrid)); + for (int j=0; j x = position(initGrid,i); + initGrid(x) = 0; for (int d=0; d x = position(grid,i); - double d = sqrt(pow(32.0-x[0],2)+pow(32.0-x[1],2)+pow(32.0-x[2],2)); - if (d<16.0) grid(i) = 2; - else grid(i) = 1; + for (int i=0; i x = position(initGrid,i); + double d = sqrt(pow(L/2-x[0],2)+pow(L/2-x[1],2)+pow(L/2-x[2],2)); + if (d x = position(grid,i); - grid(x) = 0; + int nParticles=std::max(50,(50*8192)/nodes(initGrid)); + for (int j=0; j x = position(initGrid,i); + initGrid(x) = 0; for (int d=0; d x = position(initGrid,i); initGrid(i)[0] = 0.0; initGrid(i)[1] = 0.0; - double d = 64.0-x[0]; - if (d<32.0) initGrid(i)[2] = 1.0; + double d = L/2-x[0]; + if (d x = position(initGrid,i); vector p(x); @@ -52,7 +54,8 @@ void generate(int dim, const char* filename) else initGrid(i)[1] = 1.0; } - for (int j=0; j<50; j++) { + int nParticles=std::max(50,(50*8192)/nodes(initGrid)); + for (int j=0; j x = position(initGrid,i); vector p(x); @@ -68,18 +71,20 @@ void generate(int dim, const char* filename) } if (dim==3) { - GRID3D initGrid(3,0,64,0,64,0,64); + int L=64; + GRID3D initGrid(3,0,L,0,L,0,L); for (int i=0; i x = position(initGrid,i); initGrid(i)[0] = 0.0; initGrid(i)[1] = 0.0; - double d = sqrt(pow(32.0-x[0],2)+pow(32.0-x[1],2)+pow(32.0-x[2],2)); - if (d<16.0) initGrid(i)[2] = 1.0; + double d = sqrt(pow(L/2-x[0],2)+pow(L/2-x[1],2)+pow(L/2-x[2],2)); + if (d x = position(initGrid,i); vector p(x); diff --git a/examples/coarsening/zener_pinning/isotropic/sparsePF/zener.cpp b/examples/coarsening/zener_pinning/isotropic/sparsePF/zener.cpp index 5767349..c0db839 100644 --- a/examples/coarsening/zener_pinning/isotropic/sparsePF/zener.cpp +++ b/examples/coarsening/zener_pinning/isotropic/sparsePF/zener.cpp @@ -14,16 +14,18 @@ void generate(int dim, const char* filename) { // srand() is called exactly once in MMSP.main.hpp. Do not call it here. if (dim==1) { - GRID1D initGrid(0,0,128); + int L=1024; + GRID1D initGrid(0,0,L); for (int i=0; i x = position(initGrid,i); - double d = 64.0-x[0]; - if (d<32.0) set(initGrid(i),2) = 1.0; + double d = L/2-x[0]; + if (d x = position(initGrid,i); vector p(x); @@ -48,7 +50,8 @@ void generate(int dim, const char* filename) else set(initGrid(i),1) = 1.0; } - for (int j=0; j<50; j++) { + int nParticles=std::max(50,(50*8192)/nodes(initGrid)); + for (int j=0; j x = position(initGrid,i); vector p(x); @@ -64,16 +67,18 @@ void generate(int dim, const char* filename) } if (dim==3) { - GRID3D initGrid(0,0,64,0,64,0,64); + int L=64; + GRID3D initGrid(0,0,L,0,L,0,L); for (int i=0; i x = position(initGrid,i); - double d = sqrt(pow(32.0-x[0],2)+pow(32.0-x[1],2)+pow(32.0-x[2],2)); - if (d<16.0) set(initGrid(i),2) = 1.0; + double d = sqrt(pow(L/2-x[0],2)+pow(L/2-x[1],2)+pow(L/2-x[2],2)); + if (d x = position(initGrid,i); vector p(x); diff --git a/examples/phase_transitions/cahn-hilliard/convex_splitting/cahn-hilliard.cpp b/examples/phase_transitions/cahn-hilliard/convex_splitting/cahn-hilliard.cpp index b7e6de8..9ae469b 100644 --- a/examples/phase_transitions/cahn-hilliard/convex_splitting/cahn-hilliard.cpp +++ b/examples/phase_transitions/cahn-hilliard/convex_splitting/cahn-hilliard.cpp @@ -15,7 +15,6 @@ #include"MMSP.hpp" #include #include -#include #include"cahn-hilliard.hpp" /** This code sets parameters for the evolution of two different energy configurations. @@ -65,28 +64,28 @@ double omega = 1.0; // relaxation parameter for SOR. omega=1 is stock Gauss-S // Vanilla energetics #include template inline -T energy_density(const T& C) +double energy_density(const T& C) { // Minima at C = +/- 1, local maximum at C=0 return 0.25*pow(C,4.0) - 0.5*pow(C,2.0); } template inline -T full_dfdc(const T& C) +double full_dfdc(const T& C) { return pow(C,3.0) - C; } template inline -T contractive_dfdc(const T& C) +double contractive_dfdc(const T& C) { return pow(C,3.0); } template inline -T nonlinear_coeff(const T& C) +double nonlinear_coeff(const T& C) { return pow(C,2.0); } template inline -T expansive_dfdc(const T& C) +double expansive_dfdc(const T& C) { return -C; } @@ -104,28 +103,28 @@ double QC = 3.0*(B*C0*C0 + pow(Ca,3.0) + pow(Cb,3.0)); double QD = B*pow(C0,3.0) + pow(Ca,4.0) + pow(Cb,4.0); template inline -T energy_density(const T& C) +double energy_density(const T& C) { // Minima at C = 0 and 1 return -0.5*A*pow(C-C0,2.0) + 0.25*B*pow(C-C0,4.0) + 0.25*Ca*pow(C-Ca,4.0) + 0.25*Cb*pow(C-Cb,4.0); } template inline -T full_dfdc(const T& C) +double full_dfdc(const T& C) { return -A*(C-C0) + B*pow(C-C0,3.0) + Ca*pow(C-Ca,3.0) + Cb*pow(C-Cb,3.0); } template inline -T contractive_dfdc(const T& C) +double contractive_dfdc(const T& C) { return QA*pow(C,3.0) + QC*C; } template inline -T nonlinear_coeff(const T& C) +double nonlinear_coeff(const T& C) { return QA*pow(C,2.0) + QC; } template inline -T expansive_dfdc(const T& C) +double expansive_dfdc(const T& C) { return -A*(C-C0) - QB*pow(C,2.0) - QD; } @@ -177,22 +176,61 @@ double fringe_laplacian(const grid >& GRID, const vector& x, return laplacian; } -void generate(int dim, const char* filename) +template void reportEnergy(const MMSP::grid >& GRID) { - // Initial conditions after CHiMaD Phase Field Workshop benchmark problem (October 2015) - - if (dim!=2) { - std::cerr<<"ERROR: Convex splitting discretization is only 2-D, for now."< > initGrid(2,0,2*edge,0,edge); // field 0 is c, field 1 is mu + double dV = 1.0; + for (int d=0; d x = position(GRID,n); + vector > gradC = grad(GRID, x); + double magSqGradC = 0.0; + for (int d=0; d x = position(initGrid,n); + initGrid(n)[1] = full_dfdc(initGrid(n)[0]) - K*field_laplacian(initGrid, x, 0); + } + + output(initGrid,filename); + //reportEnergy(initGrid); + } else if (dim==2) { + GRID2D initGrid(2,0,2*edge,0,edge); // field 0 is c, field 1 is mu for (int d=0; d x = position(initGrid,n); double wave = x[0]*dx(initGrid,0)*q[0] + x[1]*dx(initGrid,1)*q[1]; @@ -215,43 +254,23 @@ void generate(int dim, const char* filename) initGrid(n)[1] = full_dfdc(initGrid(n)[0]) - K*field_laplacian(initGrid, x, 0); } - double dV = 1.0; - for (int d=0; d x = position(initGrid,n); - vector > gradC = grad(initGrid, x); - double magSqGradC = 0.0; - for (int d=0; d >& oldGrid, int steps) dV *= dx(oldGrid,d); } - #ifdef DEBUG - std::ofstream ferr; - if (rank==0) - ferr.open("error.log", std::ofstream::app); - #endif - for (int step=0; step >& oldGrid, int steps) MPI::COMM_WORLD.Allreduce(&localNormB, &normB, 1, MPI_DOUBLE, MPI_SUM); #endif - #ifdef DEBUG - if (rank==0) - ferr< >& oldGrid, int steps) MMSP::Abort(-1); } + /* double energy = 0.0; double mass = 0.0; for (int n=0; n >& oldGrid, int steps) #endif if (rank==0) std::cout< Date: Fri, 26 Feb 2016 11:11:49 -0500 Subject: [PATCH 75/83] revise time estimate --- test/build_examples.sh | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/test/build_examples.sh b/test/build_examples.sh index 3e109ef..cb6bac5 100755 --- a/test/build_examples.sh +++ b/test/build_examples.sh @@ -108,7 +108,7 @@ do ;; --extra) ITERS=$((25*$ITERS)) - INTER=5000 + INTER=1000 ZED="00000" ;; --noviz) @@ -128,11 +128,16 @@ done if [[ ! $NEXEC ]] then echo ", ${DIM}-dimensional, taking $ITERS steps, using $CORES/$COREMAX MPI ranks" - if [[ $DIM -eq 3 ]] && [[ $INTER -gt 100 ]] + if [[ $DIM -eq 3 ]] && [[ $ITERS -gt 100 ]] then - DENOM=$((72*$CORES)) - RTIM=$((10*$ITERS/$DENOM)) - echo -e "${BYLW}Specified 3D test suite may take $RTIM minutes or more to complete. Consider --short.${WHT}" + DENOM=$((6*$CORES)) + if [[ $ITERS -gt 10000 ]] + then + # Not as much work at long times + DENOM=$((9*$CORES)) + fi + RTIM=$(($ITERS/$DENOM)) + echo -e "${BYLW} Expected runtime is $RTIM minutes. Consider --short.${WHT}" fi else echo From ab8ee11336837b1fd4604396e32cee07c2295819 Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Fri, 26 Feb 2016 11:55:40 -0500 Subject: [PATCH 76/83] Re-enable ghostswap in aniso MC graingrowth. Criminy. --- .../grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp index d4cc0e6..d72ef8b 100644 --- a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp +++ b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp @@ -370,7 +370,7 @@ template void update(grid& mcGrid, int steps) #ifdef MPI_VERSION MPI::COMM_WORLD.Barrier(); #endif - //ghostswap(mcGrid, sublattice); // once looped over a "color", ghostswap. + ghostswap(mcGrid, sublattice); // once looped over a "color", ghostswap. }//loop over sublattice }//loop over step }//update From c23ca764640f46543b7f7467f3b601b0e1d6c1cf Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Fri, 26 Feb 2016 16:05:22 -0500 Subject: [PATCH 77/83] tile initial condition instead of scaling up to preserve kinetics With parametrized initial conditions in grain growth and Zener pinning, increasing the grid size would decrease global curvature, therefore slowing kinetics and making go/no-go judgment on the example output less obvious. Tiling larger grids into identical subdomains preserves fast kinetics, and makes comparison of replicates easy and entertaining. --- .../anisotropic/phase_field/graingrowth.cpp | 57 ++++++++++--------- .../anisotropic/sparsePF/graingrowth.cpp | 49 +++++++++------- .../isotropic/phase_field/graingrowth.cpp | 12 ++-- .../isotropic/sparsePF/graingrowth.cpp | 12 ++-- .../anisotropic/Monte_Carlo/zener.cpp | 12 ++-- .../anisotropic/phase_field/zener.cpp | 12 ++-- .../anisotropic/sparsePF/zener.cpp | 12 ++-- .../isotropic/Monte_Carlo/zener.cpp | 12 ++-- .../isotropic/phase_field/zener.cpp | 12 ++-- .../isotropic/sparsePF/zener.cpp | 12 ++-- 10 files changed, 107 insertions(+), 95 deletions(-) diff --git a/examples/coarsening/grain_growth/anisotropic/phase_field/graingrowth.cpp b/examples/coarsening/grain_growth/anisotropic/phase_field/graingrowth.cpp index 66ef7c0..c740b20 100644 --- a/examples/coarsening/grain_growth/anisotropic/phase_field/graingrowth.cpp +++ b/examples/coarsening/grain_growth/anisotropic/phase_field/graingrowth.cpp @@ -23,35 +23,40 @@ void generate(int dim, const char* filename) vector x = position(initGrid,i); - if (x[0]3*L/4) initGrid(i)[3] = 1.0; - else initGrid(i)[0] = 1.0; + if (x[0]%128 < 32) + initGrid(i)[3] = 1.0; + else if (x[0]%128 < 64) + initGrid(i)[2] = 1.0; + else if (x[0]%128 < 96) + initGrid(i)[1] = 1.0; + else + initGrid(i)[0] = 1.0; } output(initGrid,filename); } if (dim==2) { - int L=128; + int L=256; GRID2D initGrid(4,0,L,0,L); + // Divide domain into "unit cells", 128 points on an edge for (int i=0; i x = position(initGrid,i); - if (x[0]3*L/4) { - if (x[1]3*L/4) initGrid(i)[1] = 1.0; - else initGrid(i)[0] = 1.0; + if (x[0]%128 < 32 || x[0]%128 > 96) { // less than 1/4, more than 3/4 + if ((x[1]%128) < 64) // less than 1/2 + initGrid(i)[2] = 1.0; + else + initGrid(i)[3] = 1.0; + } else { + if (x[1]%128 < 32 || x[1]%128 > 96) // less than 1/4, more than 3/4 + initGrid(i)[1] = 1.0; + else + initGrid(i)[0] = 1.0; } } @@ -62,23 +67,23 @@ void generate(int dim, const char* filename) int L=32; GRID3D initGrid(4,0,L,0,L,0,L); + // Divide domain into "unit cells", 128 points on an edge for (int i=0; i x = position(initGrid,i); - if (x[0]3*L/4) { - if (x[1]3*L/4) initGrid(i)[1] = 1.0; - else initGrid(i)[0] = 1.0; + if (x[0]%128 < 32 || x[0]%128 > 96) { // less than 1/4, more than 3/4 + if ((x[1]%128) < 64) // less than 1/2 + initGrid(i)[2] = 1.0; + else + initGrid(i)[3] = 1.0; + } else { + if (x[1]%128 < 32 || x[1]%128 > 96) // less than 1/4, more than 3/4 + initGrid(i)[1] = 1.0; + else + initGrid(i)[0] = 1.0; } } diff --git a/examples/coarsening/grain_growth/anisotropic/sparsePF/graingrowth.cpp b/examples/coarsening/grain_growth/anisotropic/sparsePF/graingrowth.cpp index 52231b7..eb08bb4 100644 --- a/examples/coarsening/grain_growth/anisotropic/sparsePF/graingrowth.cpp +++ b/examples/coarsening/grain_growth/anisotropic/sparsePF/graingrowth.cpp @@ -20,9 +20,14 @@ void generate(int dim, const char* filename) for (int i=0; i x = position(initGrid,i); - if (x[0]3*L/4) set(initGrid(i),3) = 1.0; - else set(initGrid(i),0) = 1.0; + if (x[0]%128 < 32) + set(initGrid(i),3) = 1.0; + else if (x[0]%128 < 64) + set(initGrid(i),2) = 1.0; + else if (x[0]%128 < 96) + set(initGrid(i),1) = 1.0; + else + set(initGrid(i),0) = 1.0; } output(initGrid,filename); @@ -32,20 +37,21 @@ void generate(int dim, const char* filename) int L=128; GRID2D initGrid(0,0,L,0,L); + // Divide domain into "unit cells", 128 points on an edge for (int i=0; i x = position(initGrid,i); - if (x[0]3*L/4) { - if (x[1] 96) { // less than 1/4, more than 3/4 + if (x[1]%128 < 64) // less than 1/2 + set(initGrid(i),2) = 1.0; + else + set(initGrid(i),3) = 1.0; } else { - if (x[1]3*L/4) set(initGrid(i),1) = 1.0; - else set(initGrid(i),0) = 1.0; + if (x[1]%128 < 32 || x[1]%128 > 96) // less than 1/4, more than 3/4 + set(initGrid(i),1) = 1.0; + else + set(initGrid(i),0) = 1.0; } } @@ -56,20 +62,21 @@ void generate(int dim, const char* filename) int L=32; GRID3D initGrid(0,0,L,0,L,0,L); + // Divide domain into "unit cells", 128 points on an edge for (int i=0; i x = position(initGrid,i); - if (x[0]3*L/4) { - if (x[1] 96) { // less than 1/4, more than 3/4 + if (x[1]%128 < 64) // less than 1/2 + set(initGrid(i),2) = 1.0; + else + set(initGrid(i),3) = 1.0; } else { - if (x[1]3*L/4) set(initGrid(i),1) = 1.0; - else set(initGrid(i),0) = 1.0; + if (x[1]%128 < 32 || x[1]%128 > 96) // less than 1/4, more than 3/4 + set(initGrid(i),1) = 1.0; + else + set(initGrid(i),0) = 1.0; } } diff --git a/examples/coarsening/grain_growth/isotropic/phase_field/graingrowth.cpp b/examples/coarsening/grain_growth/isotropic/phase_field/graingrowth.cpp index dfbf70e..a410ced 100644 --- a/examples/coarsening/grain_growth/isotropic/phase_field/graingrowth.cpp +++ b/examples/coarsening/grain_growth/isotropic/phase_field/graingrowth.cpp @@ -18,8 +18,8 @@ void generate(int dim, const char* filename) for (int i=0; i x = position(initGrid,i); - double d = L/2-x[0]; - if (d x = position(initGrid,i); - double d = sqrt(pow(L/2-x[0],2)+pow(L/2-x[1],2)); - if (d x = position(initGrid,i); - double d = sqrt(pow(L/2-x[0],2)+pow(L/2-x[1],2)+pow(L/2-x[2],2)); - if (d x = position(initGrid,i); - double d = L/2-x[0]; - if (d x = position(initGrid,i); - double d = sqrt(pow(L/2-x[0],2)+pow(L/2-x[1],2)); - if (d x = position(initGrid,i); - double d = sqrt(pow(L/2-x[0],2)+pow(L/2-x[1],2)+pow(L/2-x[2],2)); - if (d x = position(initGrid,i); - double d = L/2-x[0]; - if (d x = position(initGrid,i); - double d = sqrt(pow(L/2-x[0],2)+pow(L/2-x[1],2)); - if (d x = position(initGrid,i); - double d = sqrt(pow(L/2-x[0],2)+pow(L/2-x[1],2)+pow(L/2-x[2],2)); - if (d x = position(initGrid,i); initGrid(i)[0] = 0.0; initGrid(i)[1] = 0.0; - double d = L/2-x[0]; - if (d x = position(initGrid,i); initGrid(i)[0] = 0.0; initGrid(i)[1] = 0.0; - double d = sqrt(pow(L/2-x[0],2)+pow(L/2-x[1],2)); - if (d x = position(initGrid,i); initGrid(i)[0] = 0.0; initGrid(i)[1] = 0.0; - double d = sqrt(pow(L/2-x[0],2)+pow(L/2-x[1],2)+pow(L/2-x[2],2)); - if (d x = position(initGrid,i); - double d = L/2-x[0]; - if (d x = position(initGrid,i); - double d = sqrt(pow(L/2-x[0],2)+pow(L/2-x[1],2)); - if (d x = position(initGrid,i); - double d = sqrt(pow(L/2-x[0],2)+pow(L/2-x[1],2)+pow(L/2-x[2],2)); - if (d x = position(initGrid,i); - double d = L/2-x[0]; - if (d x = position(initGrid,i); - double d = sqrt(pow(L/2-x[0],2)+pow(L/2-x[1],2)); - if (d x = position(initGrid,i); - double d = sqrt(pow(L/2-x[0],2)+pow(L/2-x[1],2)+pow(L/2-x[2],2)); - if (d x = position(initGrid,i); initGrid(i)[0] = 0.0; initGrid(i)[1] = 0.0; - double d = L/2-x[0]; - if (d x = position(initGrid,i); initGrid(i)[0] = 0.0; initGrid(i)[1] = 0.0; - double d = sqrt(pow(L/2-x[0],2)+pow(L/2-x[1],2)); - if (d x = position(initGrid,i); initGrid(i)[0] = 0.0; initGrid(i)[1] = 0.0; - double d = sqrt(pow(L/2-x[0],2)+pow(L/2-x[1],2)+pow(L/2-x[2],2)); - if (d x = position(initGrid,i); - double d = L/2-x[0]; - if (d x = position(initGrid,i); - double d = sqrt(pow(L/2-x[0],2)+pow(L/2-x[1],2)); - if (d x = position(initGrid,i); - double d = sqrt(pow(L/2-x[0],2)+pow(L/2-x[1],2)+pow(L/2-x[2],2)); - if (d Date: Fri, 26 Feb 2016 16:36:35 -0500 Subject: [PATCH 78/83] correct tiling in 3D --- .../anisotropic/phase_field/graingrowth.cpp | 10 +++++----- .../grain_growth/anisotropic/sparsePF/graingrowth.cpp | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/coarsening/grain_growth/anisotropic/phase_field/graingrowth.cpp b/examples/coarsening/grain_growth/anisotropic/phase_field/graingrowth.cpp index c740b20..5082914 100644 --- a/examples/coarsening/grain_growth/anisotropic/phase_field/graingrowth.cpp +++ b/examples/coarsening/grain_growth/anisotropic/phase_field/graingrowth.cpp @@ -64,23 +64,23 @@ void generate(int dim, const char* filename) } if (dim==3) { - int L=32; + int L=64; GRID3D initGrid(4,0,L,0,L,0,L); - // Divide domain into "unit cells", 128 points on an edge + // Divide domain into "unit cells", 64 points on an edge for (int i=0; i x = position(initGrid,i); - if (x[0]%128 < 32 || x[0]%128 > 96) { // less than 1/4, more than 3/4 - if ((x[1]%128) < 64) // less than 1/2 + if (x[0]%64 < 16 || x[0]%64 > 48) { // less than 1/4, more than 3/4 + if ((x[1]%64) < 32) // less than 1/2 initGrid(i)[2] = 1.0; else initGrid(i)[3] = 1.0; } else { - if (x[1]%128 < 32 || x[1]%128 > 96) // less than 1/4, more than 3/4 + if (x[1]%64 < 16 || x[1]%64 > 48) // less than 1/4, more than 3/4 initGrid(i)[1] = 1.0; else initGrid(i)[0] = 1.0; diff --git a/examples/coarsening/grain_growth/anisotropic/sparsePF/graingrowth.cpp b/examples/coarsening/grain_growth/anisotropic/sparsePF/graingrowth.cpp index eb08bb4..5a63e50 100644 --- a/examples/coarsening/grain_growth/anisotropic/sparsePF/graingrowth.cpp +++ b/examples/coarsening/grain_growth/anisotropic/sparsePF/graingrowth.cpp @@ -59,21 +59,21 @@ void generate(int dim, const char* filename) } if (dim==3) { - int L=32; + int L=64; GRID3D initGrid(0,0,L,0,L,0,L); - // Divide domain into "unit cells", 128 points on an edge + // Divide domain into "unit cells", 64 points on an edge for (int i=0; i x = position(initGrid,i); - if (x[0]%128 < 32 || x[0]%128 > 96) { // less than 1/4, more than 3/4 - if (x[1]%128 < 64) // less than 1/2 + if (x[0]%64 < 16 || x[0]%64 > 48) { // less than 1/4, more than 3/4 + if (x[1]%64 < 32) // less than 1/2 set(initGrid(i),2) = 1.0; else set(initGrid(i),3) = 1.0; } else { - if (x[1]%128 < 32 || x[1]%128 > 96) // less than 1/4, more than 3/4 + if (x[1]%64 < 16 || x[1]%64 > 48) // less than 1/4, more than 3/4 set(initGrid(i),1) = 1.0; else set(initGrid(i),0) = 1.0; From dc472e1b31c7ff417ad02e15acd83ffd48cd96e1 Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Fri, 26 Feb 2016 16:57:27 -0500 Subject: [PATCH 79/83] MC grain growth formatting, iso/aniso sync --- .../anisotropic/Monte_Carlo/graingrowth.cpp | 4 +-- .../isotropic/Monte_Carlo/graingrowth.cpp | 26 ++++++++----------- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp index d72ef8b..6726725 100644 --- a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp +++ b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp @@ -38,7 +38,7 @@ void generate(int dim, const char* filename) output(initGrid, filename); } else if (dim == 3) { - int L=32; + int L=64; GRID3D initGrid(0, 0, 2*L, 0, L, 0, L/2); for (int i = 0; i < nodes(initGrid); i++) @@ -74,7 +74,7 @@ template void update(grid& mcGrid, int steps) vector lattice_cells_each_dimension(dim,0); for (int i = 0; i < dim; i++) { dimension_length = x1(mcGrid, i) - x0(mcGrid, i); - if (x0(mcGrid, 0) % 2 == 0) // in serial, this is always true + if (x0(mcGrid, 0) % 2 == 0) lattice_cells_each_dimension[i] = dimension_length / 2 + 1; else lattice_cells_each_dimension[i] = 1 + (dimension_length % 2 == 0 ? dimension_length / 2 : dimension_length / 2 + 1); diff --git a/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp b/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp index 311f382..1a49606 100644 --- a/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp +++ b/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp @@ -37,7 +37,7 @@ void generate(int dim, const char* filename) output(initGrid, filename); } else if (dim == 3) { - int L=32; + int L=64; GRID3D initGrid(0, 0, 2*L, 0, L, 0, L/2); for (int i = 0; i < nodes(initGrid); i++) @@ -62,11 +62,8 @@ template bool isOutsideDomain(const grid& mcGrid, const vecto template void update(grid& mcGrid, int steps) { int rank = 0; - unsigned int np = 0; #ifdef MPI_VERSION rank = MPI::COMM_WORLD.Get_rank(); - np = MPI::COMM_WORLD.Get_size(); - MPI::COMM_WORLD.Barrier(); #endif ghostswap(mcGrid); @@ -83,16 +80,17 @@ template void update(grid& mcGrid, int steps) num_lattice_cells *= lattice_cells_each_dimension[i]; } - vector x (dim, 0); - vector x_prim (dim, 0); + vector x(dim, 0); + vector x_prim(dim, 0); vector initial_coordinates(dim,0); vector num_grids_to_flip(int(pow(2, dim)),0); vector first_cell_start_coordinates(dim,0); - for (int kk = 0; kk < dim; kk++) first_cell_start_coordinates[kk] = x0(mcGrid, kk); - for (int i = 0; i < dim; i++) { - if (x0(mcGrid, i) % 2 != 0) first_cell_start_coordinates[i]--; - } + for (int kk = 0; kk < dim; kk++) + first_cell_start_coordinates[kk] = x0(mcGrid, kk); + for (int i = 0; i < dim; i++) + if (x0(mcGrid, i) % 2 != 0) + first_cell_start_coordinates[i]--; for (int j = 0; j < num_lattice_cells; j++) { @@ -105,9 +103,8 @@ template void update(grid& mcGrid, int steps) cell_coords_selected[1] = (j / lattice_cells_each_dimension[dim - 1]) % lattice_cells_each_dimension[1]; cell_coords_selected[0] = ( j / lattice_cells_each_dimension[dim - 1] ) / lattice_cells_each_dimension[1]; } - for (int i = 0; i < dim; i++) { + for (int i = 0; i < dim; i++) x[i] = first_cell_start_coordinates[i] + 2 * cell_coords_selected[i]; - } if (dim == 2) { x_prim = x; @@ -167,10 +164,9 @@ template void update(grid& mcGrid, int steps) for (int k = 0; k < dim; k++) initial_coordinates[k] = x0(mcGrid, k); - for (int i = 0; i < dim; i++) { + for (int i = 0; i < dim; i++) if (x0(mcGrid, i) % 2 != 0) initial_coordinates[i]--; - } for (int step = 0; step < steps; step++) { if (rank == 0) @@ -180,7 +176,7 @@ template void update(grid& mcGrid, int steps) else if (dim == 3) num_sublattices = 8; for (int sublattice = 0; sublattice < num_sublattices; sublattice++) { - vector x (dim, 0); + vector x(dim, 0); // This particular algorithm requires that srand() be called here. unsigned long seed=time(NULL); #ifdef MPI_VERSION From e33ff6a83cd467fcb61bd11a4c65a6ceec4324f4 Mon Sep 17 00:00:00 2001 From: Trevor Keller Date: Mon, 29 Feb 2016 12:24:25 -0500 Subject: [PATCH 80/83] swap(old,new) THEN ghostswap(old) --- .../isotropic/phase_field/graingrowth.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/coarsening/grain_growth/isotropic/phase_field/graingrowth.cpp b/examples/coarsening/grain_growth/isotropic/phase_field/graingrowth.cpp index a410ced..31805ca 100644 --- a/examples/coarsening/grain_growth/isotropic/phase_field/graingrowth.cpp +++ b/examples/coarsening/grain_growth/isotropic/phase_field/graingrowth.cpp @@ -89,24 +89,24 @@ template void update(grid >& oldGrid, int st print_progress(step, steps); #endif for (int i=0; i& phi = oldGrid(i); + // compute laplacian vector lap = laplacian(oldGrid,i); // compute sum of squares - double sum = 0.0; + T sum = 0.0; for (int j=0; j Date: Mon, 29 Feb 2016 12:55:42 -0500 Subject: [PATCH 81/83] resize for 1 minute per test --- .../grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp | 2 +- .../grain_growth/anisotropic/sparsePF/graingrowth.cpp | 2 +- .../grain_growth/isotropic/Monte_Carlo/graingrowth.cpp | 2 +- .../grain_growth/isotropic/phase_field/graingrowth.cpp | 2 +- .../grain_growth/isotropic/sparsePF/graingrowth.cpp | 2 +- .../ostwald_ripening/isotropic/phase_field/ostwald.cpp | 2 +- .../zener_pinning/anisotropic/Monte_Carlo/zener.cpp | 2 +- .../zener_pinning/anisotropic/phase_field/zener.cpp | 2 +- .../coarsening/zener_pinning/anisotropic/sparsePF/zener.cpp | 2 +- .../zener_pinning/isotropic/Monte_Carlo/zener.cpp | 2 +- .../zener_pinning/isotropic/phase_field/zener.cpp | 2 +- .../differential_equations/elliptic/Poisson/poisson.cpp | 6 +++--- .../cahn-hilliard/convex_splitting/cahn-hilliard.cpp | 3 +-- 13 files changed, 15 insertions(+), 16 deletions(-) diff --git a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp index 6726725..9dba0b5 100644 --- a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp +++ b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp @@ -30,7 +30,7 @@ void generate(int dim, const char* filename) } if (dim == 2) { - int L=128; + int L=256; GRID2D initGrid(0, 0, 2*L, 0, L); for (int i = 0; i < nodes(initGrid); i++) diff --git a/examples/coarsening/grain_growth/anisotropic/sparsePF/graingrowth.cpp b/examples/coarsening/grain_growth/anisotropic/sparsePF/graingrowth.cpp index 5a63e50..e2db013 100644 --- a/examples/coarsening/grain_growth/anisotropic/sparsePF/graingrowth.cpp +++ b/examples/coarsening/grain_growth/anisotropic/sparsePF/graingrowth.cpp @@ -34,7 +34,7 @@ void generate(int dim, const char* filename) } if (dim==2) { - int L=128; + int L=256; GRID2D initGrid(0,0,L,0,L); // Divide domain into "unit cells", 128 points on an edge diff --git a/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp b/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp index 1a49606..643013f 100644 --- a/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp +++ b/examples/coarsening/grain_growth/isotropic/Monte_Carlo/graingrowth.cpp @@ -29,7 +29,7 @@ void generate(int dim, const char* filename) } if (dim == 2) { - int L=128; + int L=256; GRID2D initGrid(0, 0, 2*L, 0, L); for (int i = 0; i < nodes(initGrid); i++) diff --git a/examples/coarsening/grain_growth/isotropic/phase_field/graingrowth.cpp b/examples/coarsening/grain_growth/isotropic/phase_field/graingrowth.cpp index 31805ca..d8355e7 100644 --- a/examples/coarsening/grain_growth/isotropic/phase_field/graingrowth.cpp +++ b/examples/coarsening/grain_growth/isotropic/phase_field/graingrowth.cpp @@ -32,7 +32,7 @@ void generate(int dim, const char* filename) } if (dim==2) { - int L=128; + int L=256; GRID2D initGrid(2,0,L,0,L); for (int i=0; i& u, const grid<3,T>& f, grid<3,T>& d, int stride) void generate(int dim, const char* filename) { if (dim==1) { - grid<1,double> initGrid(1,0,257); + grid<1,double> initGrid(1,0,2049); for (int n=0; n x = position(initGrid, n); @@ -203,7 +203,7 @@ void generate(int dim, const char* filename) } if (dim==2) { - int L=129; + int L=513; grid<2,double> initGrid(1,0,L,0,L); for (int n=0; n initGrid(1,0,L,0,L,0,L); for (int n=0; n Date: Tue, 1 Mar 2016 11:46:35 -0500 Subject: [PATCH 82/83] improved 3D builds (smaller z, cylinders instead of spheres) --- .../anisotropic/Monte_Carlo/graingrowth.cpp | 2 +- .../anisotropic/phase_field/graingrowth.cpp | 2 +- .../anisotropic/sparsePF/graingrowth.cpp | 2 +- .../isotropic/Monte_Carlo/graingrowth.cpp | 2 +- .../isotropic/phase_field/graingrowth.cpp | 14 +++++++------- .../isotropic/sparsePF/graingrowth.cpp | 18 +++++++++--------- .../isotropic/phase_field/ostwald.cpp | 2 +- .../anisotropic/Monte_Carlo/zener.cpp | 14 +++++++------- .../anisotropic/phase_field/zener.cpp | 4 ++-- .../anisotropic/sparsePF/zener.cpp | 4 ++-- .../isotropic/Monte_Carlo/zener.cpp | 14 +++++++------- .../isotropic/phase_field/zener.cpp | 4 ++-- .../zener_pinning/isotropic/sparsePF/zener.cpp | 6 +++--- .../elliptic/Poisson/poisson.cpp | 2 +- .../allen-cahn/allen-cahn.cpp | 2 +- .../convex_splitting/cahn-hilliard.cpp | 2 +- .../cahn-hilliard/explicit/cahn-hilliard.cpp | 2 +- examples/phase_transitions/model_A/model_A.cpp | 2 +- examples/phase_transitions/model_B/model_B.cpp | 2 +- .../phase_transitions/spinodal/spinodal.cpp | 2 +- .../Heisenberg/heisenberg.cpp | 2 +- examples/statistical_mechanics/Ising/ising.cpp | 4 ++-- examples/statistical_mechanics/Potts/potts.cpp | 4 ++-- 23 files changed, 56 insertions(+), 56 deletions(-) diff --git a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp index 9dba0b5..7c3c341 100644 --- a/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp +++ b/examples/coarsening/grain_growth/anisotropic/Monte_Carlo/graingrowth.cpp @@ -39,7 +39,7 @@ void generate(int dim, const char* filename) output(initGrid, filename); } else if (dim == 3) { int L=64; - GRID3D initGrid(0, 0, 2*L, 0, L, 0, L/2); + GRID3D initGrid(0, 0, 2*L, 0, L, 0, L/4); for (int i = 0; i < nodes(initGrid); i++) initGrid(i) = rand() % 20; diff --git a/examples/coarsening/grain_growth/anisotropic/phase_field/graingrowth.cpp b/examples/coarsening/grain_growth/anisotropic/phase_field/graingrowth.cpp index 5082914..131eee5 100644 --- a/examples/coarsening/grain_growth/anisotropic/phase_field/graingrowth.cpp +++ b/examples/coarsening/grain_growth/anisotropic/phase_field/graingrowth.cpp @@ -65,7 +65,7 @@ void generate(int dim, const char* filename) if (dim==3) { int L=64; - GRID3D initGrid(4,0,L,0,L,0,L); + GRID3D initGrid(4,0,2*L,0,L,0,L/4); // Divide domain into "unit cells", 64 points on an edge for (int i=0; i x = position(initGrid,i); - double d = 32-x[0]%64; - if (d<16.0) { + double r = 32-x[0]%64; + if (r<16.0) { initGrid(i)[0] = 0.0; initGrid(i)[1] = 1.0; } else { @@ -37,8 +37,8 @@ void generate(int dim, const char* filename) for (int i=0; i x = position(initGrid,i); - double d = sqrt(pow(32-x[0]%64,2)+pow(32-x[1]%64,2)); - if (d<16.0) { + double r = sqrt(pow(32-x[0]%64,2)+pow(32-x[1]%64,2)); + if (r<16.0) { initGrid(i)[0] = 0.0; initGrid(i)[1] = 1.0; } else { @@ -52,12 +52,12 @@ void generate(int dim, const char* filename) if (dim==3) { int L=64; - GRID3D initGrid(2,0,L,0,L,0,L); + GRID3D initGrid(2,0,2*L,0,L,0,L/4); for (int i=0; i x = position(initGrid,i); - double d = sqrt(pow(32-x[0]%64,2)+pow(32-x[1]%64,2)+pow(32-x[2]%64,2)); - if (d<16.0) { + double r = sqrt(pow(32-x[0]%64,2)+pow(32-x[1]%64,2)); + if (r<16.0) { initGrid(i)[0] = 0.0; initGrid(i)[1] = 1.0; } else { diff --git a/examples/coarsening/grain_growth/isotropic/sparsePF/graingrowth.cpp b/examples/coarsening/grain_growth/isotropic/sparsePF/graingrowth.cpp index cc64d78..e4ad04e 100644 --- a/examples/coarsening/grain_growth/isotropic/sparsePF/graingrowth.cpp +++ b/examples/coarsening/grain_growth/isotropic/sparsePF/graingrowth.cpp @@ -18,8 +18,8 @@ void generate(int dim, const char* filename) for (int i=0; i x = position(initGrid,i); - double d = 32-x[0]; - if (d<16.0) set(initGrid(i),1)= 1.0; + double r = 32-x[0]; + if (r<16.0) set(initGrid(i),1)= 1.0; else set(initGrid(i),0) = 1.0; } @@ -27,13 +27,13 @@ void generate(int dim, const char* filename) } if (dim==2) { - int L=256; - GRID2D initGrid(0,0,L,0,L); + int L=128; + GRID2D initGrid(0,0,2*L,0,L); for (int i=0; i x = position(initGrid,i); - double d = sqrt(pow(32-x[0]%64,2)+pow(32-x[1]%64,2)); - if (d<16.0) set(initGrid(i),1)= 1.0; + double r = sqrt(pow(32-x[0]%64,2)+pow(32-x[1]%64,2)); + if (r<16.0) set(initGrid(i),1)= 1.0; else set(initGrid(i),0) = 1.0; } @@ -42,12 +42,12 @@ void generate(int dim, const char* filename) if (dim==3) { int L=64; - GRID3D initGrid(0,0,L,0,L,0,L); + GRID3D initGrid(0,0,2*L,0,L,0,L/4); for (int i=0; i x = position(initGrid,i); - double d = sqrt(pow(32-x[0]%64,2)+pow(32-x[1]%64,2)+pow(32-x[2]%64,2)); - if (d<16.0) set(initGrid(i),1)= 1.0; + double r = sqrt(pow(32-x[0]%64,2)+pow(32-x[1]%64,2)); + if (r<16.0) set(initGrid(i),1)= 1.0; else set(initGrid(i),0) = 1.0; } diff --git a/examples/coarsening/ostwald_ripening/isotropic/phase_field/ostwald.cpp b/examples/coarsening/ostwald_ripening/isotropic/phase_field/ostwald.cpp index 8b68ff2..2857466 100644 --- a/examples/coarsening/ostwald_ripening/isotropic/phase_field/ostwald.cpp +++ b/examples/coarsening/ostwald_ripening/isotropic/phase_field/ostwald.cpp @@ -41,7 +41,7 @@ void generate(int dim, const char* filename) if (dim==3) { int L=64; - GRID3D initGrid(2,0,2*L,0,L,0,L/2); + GRID3D initGrid(2,0,2*L,0,L,0,L/4); for (int n=0; n x = position(initGrid,i); - double d = 32-x[0]%64; - if (d<16.0) initGrid(i) = 2; + double r = 32-x[0]%64; + if (r<16.0) initGrid(i) = 2; else initGrid(i) = 1; } @@ -43,8 +43,8 @@ void generate(int dim, const char* filename) for (int i=0; i x = position(initGrid,i); - double d = sqrt(pow(32-x[0]%64,2)+pow(32-x[1]%64,2)); - if (d<16.0) initGrid(i) = 2; + double r = sqrt(pow(32-x[0]%64,2)+pow(32-x[1]%64,2)); + if (r<16.0) initGrid(i) = 2; else initGrid(i) = 1; } @@ -63,12 +63,12 @@ void generate(int dim, const char* filename) if (dim==3) { int L=64; - GRID3D initGrid(0,0,L,0,L,0,L); + GRID3D initGrid(0,0,2*L,0,L,0,L/2); for (int i=0; i x = position(initGrid,i); - double d = sqrt(pow(32-x[0]%64,2)+pow(32-x[1]%64,2)+pow(32-x[2]%64,2)); - if (d<16.0) initGrid(i) = 2; + double r = sqrt(pow(32-x[0]%64,2)+pow(32-x[1]%64,2)); + if (r<16.0) initGrid(i) = 2; else initGrid(i) = 1; } diff --git a/examples/coarsening/zener_pinning/anisotropic/phase_field/zener.cpp b/examples/coarsening/zener_pinning/anisotropic/phase_field/zener.cpp index 368776a..57456d9 100644 --- a/examples/coarsening/zener_pinning/anisotropic/phase_field/zener.cpp +++ b/examples/coarsening/zener_pinning/anisotropic/phase_field/zener.cpp @@ -73,13 +73,13 @@ void generate(int dim, const char* filename) if (dim==3) { int L=64; - GRID3D initGrid(3,0,L,0,L,0,L); + GRID3D initGrid(3,0,2*L,0,L,0,L/4); for (int i=0; i x = position(initGrid,i); initGrid(i)[0] = 0.0; initGrid(i)[1] = 0.0; - double d = sqrt(pow(32-x[0]%64,2)+pow(32-x[1]%64,2)+pow(32-x[2]%64,2)); + double d = sqrt(pow(32-x[0]%64,2)+pow(32-x[1]%64,2)); if (d<16.0) initGrid(i)[2] = 1.0; else initGrid(i)[1] = 1.0; } diff --git a/examples/coarsening/zener_pinning/anisotropic/sparsePF/zener.cpp b/examples/coarsening/zener_pinning/anisotropic/sparsePF/zener.cpp index 4465e67..1e828e1 100644 --- a/examples/coarsening/zener_pinning/anisotropic/sparsePF/zener.cpp +++ b/examples/coarsening/zener_pinning/anisotropic/sparsePF/zener.cpp @@ -69,11 +69,11 @@ void generate(int dim, const char* filename) if (dim==3) { int L=64; - GRID3D initGrid(0,0,L,0,L,0,L); + GRID3D initGrid(0,0,2*L,0,L,0,L/4); for (int i=0; i x = position(initGrid,i); - double d = sqrt(pow(32-x[0]%64,2)+pow(32-x[1]%64,2)+pow(32-x[2]%64,2)); + double d = sqrt(pow(32-x[0]%64,2)+pow(32-x[1]%64,2)); if (d<16.0) set(initGrid(i),2) = 1.0; else set(initGrid(i),1) = 1.0; } diff --git a/examples/coarsening/zener_pinning/isotropic/Monte_Carlo/zener.cpp b/examples/coarsening/zener_pinning/isotropic/Monte_Carlo/zener.cpp index d803d09..e995ecd 100644 --- a/examples/coarsening/zener_pinning/isotropic/Monte_Carlo/zener.cpp +++ b/examples/coarsening/zener_pinning/isotropic/Monte_Carlo/zener.cpp @@ -19,8 +19,8 @@ void generate(int dim, const char* filename) for (int i=0; i x = position(initGrid,i); - double d = 32-x[0]%64; - if (d<16.0) initGrid(i) = 2; + double r = 32-x[0]%64; + if (r<16.0) initGrid(i) = 2; else initGrid(i) = 1; } @@ -47,8 +47,8 @@ void generate(int dim, const char* filename) for (int i=0; i x = position(initGrid,i); - double d = sqrt(pow(32-x[0]%64,2)+pow(32-x[1]%64,2)); - if (d<16.0) initGrid(i) = 2; + double r = sqrt(pow(32-x[0]%64,2)+pow(32-x[1]%64,2)); + if (r<16.0) initGrid(i) = 2; else initGrid(i) = 1; } @@ -71,12 +71,12 @@ void generate(int dim, const char* filename) if (dim==3) { int L=64; - GRID3D initGrid(0,0,L,0,L,0,L); + GRID3D initGrid(0,0,2*L,0,L,0,L/4); for (int i=0; i x = position(initGrid,i); - double d = sqrt(pow(32-x[0]%64,2)+pow(32-x[1]%64,2)+pow(32-x[2]%64,2)); - if (d<16.0) initGrid(i) = 2; + double r = sqrt(pow(32-x[0]%64,2)+pow(32-x[1]%64,2)); + if (r<16.0) initGrid(i) = 2; else initGrid(i) = 1; } diff --git a/examples/coarsening/zener_pinning/isotropic/phase_field/zener.cpp b/examples/coarsening/zener_pinning/isotropic/phase_field/zener.cpp index 6342a6b..27cfa08 100644 --- a/examples/coarsening/zener_pinning/isotropic/phase_field/zener.cpp +++ b/examples/coarsening/zener_pinning/isotropic/phase_field/zener.cpp @@ -72,13 +72,13 @@ void generate(int dim, const char* filename) if (dim==3) { int L=64; - GRID3D initGrid(3,0,L,0,L,0,L); + GRID3D initGrid(3,0,2*L,0,L,0,L/4); for (int i=0; i x = position(initGrid,i); initGrid(i)[0] = 0.0; initGrid(i)[1] = 0.0; - double d = sqrt(pow(32-x[0]%64,2)+pow(32-x[1]%64,2)+pow(32-x[2]%64,2)); + double d = sqrt(pow(32-x[0]%64,2)+pow(32-x[1]%64,2)); if (d<16.0) initGrid(i)[2] = 1.0; else initGrid(i)[1] = 1.0; } diff --git a/examples/coarsening/zener_pinning/isotropic/sparsePF/zener.cpp b/examples/coarsening/zener_pinning/isotropic/sparsePF/zener.cpp index a012963..d8d1c0e 100644 --- a/examples/coarsening/zener_pinning/isotropic/sparsePF/zener.cpp +++ b/examples/coarsening/zener_pinning/isotropic/sparsePF/zener.cpp @@ -41,7 +41,7 @@ void generate(int dim, const char* filename) if (dim==2) { int L=128; - GRID2D initGrid(0,0,L,0,L); + GRID2D initGrid(0,0,2*L,0,L); for (int i=0; i x = position(initGrid,i); @@ -68,11 +68,11 @@ void generate(int dim, const char* filename) if (dim==3) { int L=64; - GRID3D initGrid(0,0,L,0,L,0,L); + GRID3D initGrid(0,0,2*L,0,L,0,L/4); for (int i=0; i x = position(initGrid,i); - double d = sqrt(pow(32-x[0]%64,2)+pow(32-x[1]%64,2)+pow(32-x[2]%64,2)); + double d = sqrt(pow(32-x[0]%64,2)+pow(32-x[1]%64,2)); if (d<16.0) set(initGrid(i),2) = 1.0; else set(initGrid(i),1) = 1.0; } diff --git a/examples/differential_equations/elliptic/Poisson/poisson.cpp b/examples/differential_equations/elliptic/Poisson/poisson.cpp index 4d6958a..4828f59 100644 --- a/examples/differential_equations/elliptic/Poisson/poisson.cpp +++ b/examples/differential_equations/elliptic/Poisson/poisson.cpp @@ -217,7 +217,7 @@ void generate(int dim, const char* filename) } if (dim==3) { - int L=127; + int L=65; grid<3,double> initGrid(1,0,L,0,L,0,L); for (int n=0; n Date: Sun, 22 Jan 2017 17:27:22 -0500 Subject: [PATCH 83/83] fix MC parallel bug, Heisenberg spelling --- .../anisotropic/Monte_Carlo/zener.cpp | 10 +- .../isotropic/Monte_Carlo/zener.cpp | 125 ++++++++++-------- .../Heisenberg/heisenberg.cpp | 8 +- 3 files changed, 78 insertions(+), 65 deletions(-) diff --git a/examples/coarsening/zener_pinning/anisotropic/Monte_Carlo/zener.cpp b/examples/coarsening/zener_pinning/anisotropic/Monte_Carlo/zener.cpp index 450a727..b534ba1 100644 --- a/examples/coarsening/zener_pinning/anisotropic/Monte_Carlo/zener.cpp +++ b/examples/coarsening/zener_pinning/anisotropic/Monte_Carlo/zener.cpp @@ -1,13 +1,13 @@ // zener.cpp -// Anisotropic coarsening algorithms for 2D and 3D Monte Carlo methods +// Algorithms for anisotropic 2D and 3D Monte Carlo grain growth with Zener pinning // Questions/comments to gruberja@gmail.com (Jason Gruber) #ifndef ZENER_UPDATE #define ZENER_UPDATE +#include #include"MMSP.hpp" -#include"anisotropy.hpp" #include"zener.hpp" -#include +#include"anisotropy.hpp" namespace MMSP{ @@ -98,8 +98,8 @@ template void update(grid& mcGrid, int steps) const double kT = (dim==3)?0.75:0.50; int gss = int(nodes(mcGrid)); - // srand() is called exactly once in MMSP.main.hpp. Do not call it here. + for (int step=0; step void update(grid& mcGrid, int steps) } } -} // namespace MC +} // namespace MMSP #endif diff --git a/examples/coarsening/zener_pinning/isotropic/Monte_Carlo/zener.cpp b/examples/coarsening/zener_pinning/isotropic/Monte_Carlo/zener.cpp index e995ecd..775a0dd 100644 --- a/examples/coarsening/zener_pinning/isotropic/Monte_Carlo/zener.cpp +++ b/examples/coarsening/zener_pinning/isotropic/Monte_Carlo/zener.cpp @@ -1,11 +1,11 @@ -// graingrowth.cpp +// zener.cpp // Algorithms for 2D and 3D Monte Carlo grain growth with Zener pinning // Questions/comments to gruberja@gmail.com (Jason Gruber) -#ifndef MCGRID_UPDATE -#define MCGRID_UPDATE -#include"MMSP.hpp" +#ifndef ZENER_UPDATE +#define ZENER_UPDATE #include +#include"MMSP.hpp" #include"zener.hpp" namespace MMSP{ @@ -28,14 +28,9 @@ void generate(int dim, const char* filename) for (int j=0; j x = position(initGrid,i); - initGrid(x) = 0; - for (int d=0; d p(x); + for (p[0]=x[0]-1; p[0]<=x[0]+1; p[0]++) + initGrid(p) = 0; } output(initGrid,filename); @@ -56,14 +51,10 @@ void generate(int dim, const char* filename) for (int j=0; j x = position(initGrid,i); - initGrid(x) = 0; - for (int d=0; d p(x); + for (p[0]=x[0]-1; p[0]<=x[0]+1; p[0]++) + for (p[1]=x[1]-1; p[1]<=x[1]+1; p[1]++) + initGrid(p) = 0; } output(initGrid,filename); @@ -71,7 +62,7 @@ void generate(int dim, const char* filename) if (dim==3) { int L=64; - GRID3D initGrid(0,0,2*L,0,L,0,L/4); + GRID3D initGrid(0,0,2*L,0,L,0,L/2); for (int i=0; i x = position(initGrid,i); @@ -84,53 +75,76 @@ void generate(int dim, const char* filename) for (int j=0; j x = position(initGrid,i); - initGrid(x) = 0; - for (int d=0; d p(x); + for (p[0]=x[0]-1; p[0]<=x[0]+1; p[0]++) + for (p[1]=x[1]-1; p[1]<=x[1]+1; p[1]++) + for (p[2]=x[2]-1; p[2]<=x[2]+1; p[2]++) + initGrid(p) = 0; } output(initGrid,filename); } } -template void update(grid& spinGrid, int steps) +template void update(grid& mcGrid, int steps) { int rank=0; #ifdef MPI_VERSION rank = MPI::COMM_WORLD.Get_rank(); #endif - ghostswap(spinGrid); + ghostswap(mcGrid); const double kT = (dim==3)?0.75:0.50; - int gss = int(sqrt(nodes(spinGrid))); + int gss = int(nodes(mcGrid)); // srand() is called exactly once in MMSP.main.hpp. Do not call it here. for (int step=0; step x = position(mcGrid,p); + int spin1 = mcGrid(p); if (spin1!=0) { - vector x = position(spinGrid,p); // determine neighboring spins sparse neighbors; - set(neighbors,spinGrid(x)) = true; - for (int d=0; d<1; d++) { - x[d]--; - set(neighbors,spinGrid(x)) = true; - x[d]+=2; - set(neighbors,spinGrid(x)) = true; - x[d]--; + if (dim==1) { + for (int i=-1; i<2; i++) { + x[0] += i; + int spin = mcGrid(x); + set(neighbors,spin) = true; + x[0] -= i; + } + } else if (dim==2) { + for (int i=-1; i<2; i++) { + x[0] += i; + for (int j=-1; j<2; j++) { + x[1] += j; + int spin = mcGrid(x); + set(neighbors,spin) = true; + x[1] -= j; + } + x[0] -= i; + } + } else if (dim==3) { + for (int i=-1; i<2; i++) { + x[0] += i; + for (int j=-1; j<2; j++) { + x[1] += j; + for (int k=-1; k<2; k++) { + x[2] += k; + int spin = mcGrid(x); + set(neighbors,spin) = true; + x[2] -= k; + } + x[1] -= j; + } + x[0] -= i; + } } // choose a random neighbor spin @@ -140,31 +154,31 @@ template void update(grid& spinGrid, int steps) // compute energy change double dE = -1.0; if (dim==1) { - for (int i=-1; i<=1; i++) { + for (int i=-1; i<2; i++) { x[0] += i; - int spin = spinGrid(x); + int spin = mcGrid(x); dE += (spin!=spin2)-(spin!=spin1); x[0] -= i; } } else if (dim==2) { - for (int i=-1; i<=1; i++) { + for (int i=-1; i<2; i++) { x[0] += i; - for (int j=-1; j<=1; j++) { + for (int j=-1; j<2; j++){ x[1] += j; - int spin = spinGrid(x); + int spin = mcGrid(x); dE += (spin!=spin2)-(spin!=spin1); x[1] -= j; } x[0] -= i; } } else if (dim==3) { - for (int i=-1; i<=1; i++) { + for (int i=-1; i<2; i++) { x[0] += i; - for (int j=-1; j<=1; j++) { + for (int j=-1; j<2; j++) { x[1] += j; - for (int k=-1; k<=1; k++) { + for (int k=-1; k<2; k++) { x[2] += k; - int spin = spinGrid(x); + int spin = mcGrid(x); dE += (spin!=spin2)-(spin!=spin1); x[2] -= k; } @@ -177,13 +191,13 @@ template void update(grid& spinGrid, int steps) // attempt a spin flip double r = double(rand())/double(RAND_MAX); if (dE<=0.0) - spinGrid(p) = spin2; + mcGrid(p) = spin2; else if (r void update(grid& spinGrid, int steps) #endif #include"MMSP.main.hpp" - diff --git a/examples/statistical_mechanics/Heisenberg/heisenberg.cpp b/examples/statistical_mechanics/Heisenberg/heisenberg.cpp index 1c595c7..b8d8056 100644 --- a/examples/statistical_mechanics/Heisenberg/heisenberg.cpp +++ b/examples/statistical_mechanics/Heisenberg/heisenberg.cpp @@ -1,9 +1,9 @@ -// spinenberg.cpp -// 2D and 3D spinenberg model +// heisenberg.cpp +// 2D and 3D heisenberg model // Questions/comments to gruberja@gmail.com (Jason Gruber) -#ifndef spinENBERG_UPDATE -#define spinENBERG_UPDATE +#ifndef HEISENBERG_UPDATE +#define HEISENBERG_UPDATE #include"MMSP.hpp" #include #include"heisenberg.hpp"