ebook img

Object-oriented implementations of the MPDATA advection equation solver in C++, Python and ... PDF

19 Pages·2013·0.38 MB·English
by  
Save to my drive
Quick download
Download
Most books are stored in the elastic cloud where traffic is expensive. For this reason, we have a limit on daily download.

Preview Object-oriented implementations of the MPDATA advection equation solver in C++, Python and ...

Object-oriented implementations of the MPDATA advection equation solver in C++, Python and Fortran SylwesterArabasa,DorotaJareckaa,AnnaJarugaa,MaciejFijałkowskib aInstituteofGeophysics,FacultyofPhysics,UniversityofWarsaw bPyPyTeam 3 1 0 Abstract 2 r Three object-oriented implementations of a prototype solver of the advection equation are introduced. The presented programs aare based on Blitz++ (C++), NumPy (Python), and Fortran’s built-in array containers. The solvers include an implementation M of the MultidimensionalPositive-Definite Advective TransportAlgorithm (MPDATA). The introduced codes exemplify how the applicationofobject-orientedprogramming(OOP)techniquesallowstoreproducethemathematicalnotationusedintheliterature 9 within the program code. A discussion on the tradeoffs of the programming language choice is presented. The main angles 1 of comparison are code brevity and syntax clarity (and hence maintainability and auditability) as well as performance. In the ]case of Python, a significant performancegain is observed when switching from the standard interpreter (CPython) to the PyPy h implementationofPython. Entiresourcecodeofallthreeimplementationsisembeddedinthetextandislicensedundertheterms p -oftheGNUGPLlicense. p mKeywords: object-orientedprogramming,advectionequation,MPDATA,C++,Fortran,Python o c .Contents 1. Introduction s c i1 Introduction 1 Object oriented programming (OOP) ”has become recog- s y nised as the almost unique successful paradigm for creating h2 Implementation 2 complexsoftware”[1,Sec.1.3]. Itisintriguingthat,whilethe p 2.1 Arraycontainers. . . . . . . . . . . . . . . . . 2 quotedstatementcomesfromtheverybooksubtitledTheArtof [ 2.2 Containersforsequencesofarrays . . . . . . . 3 ScientificComputing,hardlyany(ifnotnone)ofthecurrently 2 2.3 Staggeredgrid . . . . . . . . . . . . . . . . . . 4 operational weather and climate prediction systems - flagship v 2.4 Haloregions. . . . . . . . . . . . . . . . . . . 5 examplesof complex scientific software - make extensive use 4 2.5 Arrayindexpermutations . . . . . . . . . . . . 5 ofOOPtechniques. Fortranhasbeenthelanguageofchoicein 3 3 2.6 Prototypesolver . . . . . . . . . . . . . . . . . 6 oceanic[2], weather-prediction[3]andEarthsystem[4]mod- 1 2.7 Periodicboundaries(C++) . . . . . . . . . . . 8 elling,andnoneofits20-centuryeditionswereobject-oriented 1. 2.8 Donor-cellformulæ(C++) . . . . . . . . . . . 8 languages[seee.g.5,fordiscussion]. ApplicationofOOPtechniquesindevelopmentofnumerical 0 2.9 Donor-cellsolver(C++) . . . . . . . . . . . . 9 3 2.10 MPDATAformulæ(C++) . . . . . . . . . . . 9 modellingsoftwaremayhelpto: 1 2.11 MPDATAsolver(C++) . . . . . . . . . . . . . 10 : (i) maintain modularity and separation of programlogic lay- v 2.12 Usageexample(C++) . . . . . . . . . . . . . 10 ers(e.g.separationofnumericalalgorithms,parallelisation i X mechanisms,datainput/output,errorhandlingandthede- 3 Performanceevaluation 12 r scriptionofphysicalprocesses);and a 4 Discussiononthetradeoffsoflanguagechoice 13 (ii) shorten and simplify the source code and improve its 4.1 OOPforblackboardabstractions . . . . . . . . 14 readabilitybyreproducingwithintheprogramlogicthe 4.2 Performance . . . . . . . . . . . . . . . . . . . 14 mathematicalnotationusedintheliterature. 4.3 Easeofuseandabuse . . . . . . . . . . . . . . 14 4.4 Addedvalues . . . . . . . . . . . . . . . . . . 14 The first application is attainable, yet arguably cumbersome, withproceduralprogramming. Thelatter,virtuallyimpossible 5 Summaryandoutlook 15 toobtainwithproceduralprogramming,isthefocusofthispa- per. It also enables the compiler or library authors to relieve Appendix P Pythoncodeforsections2.7–2.11 16 theuser(i.e. scientificprogrammer)fromhand-codingoptimi- sations,apracticelongrecognisedashavingastrongnegative Appendix F Fortrancodeforsections2.7–2.11 17 impactwhendebuggingandmaintenanceareconsidered[6]. PreprintsubmittedtoComputerPhysicsCommunications December11,2013 MPDATA [7] stands for MultidimensionalPositive Definite Programminglanguageconstructswheninlinedin thetextare AdvectiveTransportAlgorithmandisanexampleofanumer- typesetinbold,e.g. GOTO2. ical procedure used in weather, climate and ocean simulation systems [e.g. 8, 9, 10, respectively]. MPDATA is a solver for 2. Implementation systemsofadvectionequationsofthefollowingform: Doubleprecisionfloating-pointformatisusedinallthreeim- ∂ψ=−∇·(~vψ) (1) t plementations.Thecodesbeginwiththefollowingdefinitions: that describe evolution of a scalar field ψ transported by the listing C.1 (C++) 3 typedef double real_t; fluid flow with velocity ~v. Quoting Numerical Recipes once more,developmentofmethodstonumericallysolvesuchprob- listing P.1 (Python) 3 real_t = ’float64’ lems”isanartasmuchasascience”[1,Sec.20.1],andMP- DATA is an example of the state-of-the art in this field. MP- listing F.1 (Fortran) 3 module real_m DATA is designed to accurately solve equation (1) in an ar- 4 implicit none bitrarynumberofdimensionsassuringpositive-definitenessof 5 integer, parameter :: real_t = kind(0.d0) 6 end module scalarfieldψandincurringsmallnumericaldiffusion.Allrele- vantMPDATAformulæaregiveninthetextbutarepresented whichprovideaconvenientwayofswitchingtodifferentpreci- without derivationor detailed discussion. For a recent review sion. ofMPDATA-basedtechniquesseeSmolarkiewicz[11,andref- Allcodesarestructuredinawayallowingcompilationofthe erencestherein]. code in exactly the same order as presented in the text within Inthispaperweintroduceanddiscussobject-orientedimple- one sourcefile, hence everyFortranlisting containsdefinition mentationsofanMPDATA-basedtwo-dimensional(2D)advec- ofaseparatemodule. tionequationsolverwritteninC++11(ISO/IEC14882:2011), Python [13] and Fortran 2008 (ISO/IEC 1539-1:2010). In 2.1. Arraycontainers the following section we introduce the three implementations brieflydescribingthealgorithmitselfanddiscussingwhereand Solutionof equation(1) using MPDATA impliesdiscretisa- how the OOP techniques may be applied in its implementa- tion onto a grid of the ψ and the Courant numberC~ = ~v· ∆t ∆x tion. ThesyntaxandnomenclatureofOOPtechniquesareused fields, where∆t isthesolvertimestepand∆x isthegridspac- without introduction, for an overview of OOP in context of ing. C++, Python and Fortran, consult for example [15, Part II], Presented C++ implementation of MPDATA is built upon [16, Chapter 5] and [17, Chapter 11], respectively. The third the Blitz++ library1. Blitz offers object-oriented representa- sectionofthispapercoversperformanceevaluationofthethree tion of n-dimensional arrays, and array-valued mathematical implementations. The fourth section covers discussion of the expressions. Inparticular,itoffersloop-freenotationforarray tradeoffs of the programminglanguage choice. The fifth sec- arithmeticsthatdoesnotincurcreationofintermediatetempo- tionclosesthearticlewithabriefsummary. rary objects. Blitz++ is a header-only library2 – to use it, it Throughoutthe paperwepresentthethreeimplementations isenoughtoincludetheappropriateheaderfile,andoptionally by discussing source code listings which coverthe entire pro- exposetherequiredclassestothepresentnamespace: gram code. Subsections 2.1-2.6 describe all three implemen- listing C.2 (C++) 4 #include <blitz/array.h> tations,whilesubsequentsections2.7-2.12coverdiscussionof 5 using arr_t = blitz::Array<real_t, 2>; C++codeonly.TherelevantpartsofPythonandFortrancodes 6 using rng_t = blitz::Range; 7 using idx_t = blitz::RectDomain<2>; do not differ significantly, and for readability reasons are pre- sentedinAppendix PandAppendix F,respectively. Here arr_t, rng_t and idx_t serve as alias identifiers and are TheentirecodeislicensedunderthetermsoftheGNUGen- introducedinordertoshortenthecode. eralPublicLicenselicenseversion3[18]. ThepowerofBlitz++ comesfromthe abilitytoexpressar- All listings include line numbers printed to the left of the ray expressions as objects. In particular, it is possible to de- source code, with separate numbering for C++ (listings pre- fine a function that returns an array expression; i.e. not the fixedwithC,blackframe), resultant array, but an object representing a „recipe” defining listing C.0 (C++) 1 // code licensed under the terms of GNU GPL v3 theoperationstobeperformedonthearguments. Asa conse- 2 // copyright holder: University of Warsaw quence,the returntypesof suchfunctionsbecomeunintelligi- ble. Luckily,theautoreturntypedeclarationfromtheC++11 Python(listingsprefixedwithP,blueframe)and standardallowstosimplifythecodesignificantly,evenmoreif listing P.0 (Python) 1 # code licensed under the terms of GNU GPL v3 usedthroughthefollowingpreprocessormacro: 2 # copyright holder: University of Warsaw Fortran(listingsprefixedwithF,redframe). 1Blitz++ is a C++ class library for scientific computing which uses listing F.0 (Fortran) the expression templates technique to achieve high performance, see 1 ! code licensed under the terms of GNU GPL v3 http://sf.net/projects/blitz/ 2 ! copyright holder: University of Warsaw 2Blitz++requireslinkingwithlibblitzifdebuggingmodeisused 2 listing C.3 (C++) C[x] and C[y] arrays constituting the sequence have different 8 #define return_macro(expr) \ sizes (see discussion of the Arakawa-C grid in section 2.3). 9 -> decltype(safeToReturn(expr)) \ 10 { return safeToReturn(expr); } Second,theorderofdimensionswouldneedtobedifferentfor differentlanguagesto assure that the contiguousdimension is The call to blitz::safeToReturn() function is included in or- usedforoneofthespacedimensionsandnotfortimelevels. der to ensure that all arrays involved in the expression being In the C++ implementation the Boost5 ptr_vector class returnedcontinuetoexistinthecallerscope.Forexample,def- is used to represent sequences of Blitz++ arrays and at the inition of a function returning its array-valued argumentdou- same time to handle automatic freeing of dynamically allo- bled, reads: auto f(arr_t x) return_macro(2*x). This is the cated memory. The ptr_vectorclass is furthercustomisedby onlypreprocessormacrodefinedherein. defininga derivedstructure which element-access[ ] operator For the Python implementation of MPDATA the NumPy3 isoverloadedwithamodulovariant: package is used. In order to make the code compatible with listing C.4 (C++) 11 #include <boost/ptr_container/ptr_vector.hpp> boththe standardCPythonaswellasthe alternativePyPyim- 12 struct arrvec_t : boost::ptr_vector<arr_t> plementationofPython[19],thePythoncodeincludesthefol- 13 { 14 const arr_t &operator[](const int i) const lowingsequenceofimportstatements: 15 { listing P.2 (Python) 16 return this->at((i + this->size()) % this->size()); 4 try: 17 } 5 import numpypy 18 }; 6 from _numpypy.pypy import set_invalidation 7 set_invalidation(False) Consequently the last element of any such sequence may be 8 except ImportError: 9 pass accessedatindex-1,thelastbutoneat-2,andsoon. 10 import numpy InthePythonimplementationthebuilt-intupletypeisused 11 try: 12 numpy.seterr(all=’ignore’) tostoresequencesofNumPyarrays. Employmentofnegative 13 except AttributeError: indices for handling from-the-endaddressing of elements is a 14 pass built-infeatureofallsequencecontainersinPython. First, the PyPy’s built-in NumPy implementation named Fortrandoesnotfeatureanybuilt-insequencecontainerca- numpypy is imported if applicable (i.e. if running PyPy), pableofstoringarrays,henceacustomarrvec_ttypeisintro- and the lazy evaluation mode is turned on through the duced: set_invalidation(False) call. PyPy’s lazy evaluation obtained listing F.2 (Fortran) 7 module arrvec_m with the help of a just-in-time compiler enables to achieve an 8 use real_m analogous to Blitz++ temporary-array-freehandling of array- 9 implicit none 10 valued expressions (see discussion in section 3). Second, to 11 type :: arr_t match the settingsof C++ and Fortrancompilersused herein, 12 real(real_t), allocatable :: a(:,:) 13 end type the NumPy package is instructed to ignore any floating-point 14 errors, if such an option is available in the interpreter4. The 15 type :: arrptr_t 16 class(arr_t), pointer :: p above lines conclude all code modificationsthat needed to be 17 end type addedinordertorunthecodewithPyPy. 18 19 type :: arrvec_t Among the three considered languages only Fortran is 20 class(arr_t), allocatable :: arrs(:) equippedwithbuilt-inarrayhandlingfacilitiesofpracticaluse 21 class(arrptr_t), allocatable :: at(:) 22 integer :: length in high-performance computing. Therefore, there is no need 23 contains forusinganexternalpackageaswithC++andPython.Fortran 24 procedure :: ctor => arrvec_ctor 25 procedure :: init => arrvec_init array-handlingfeaturesarenotobject-oriented,though. 26 end type 27 28 contains 2.2. Containersforsequencesofarrays 29 30 subroutine arrvec_ctor(this, n) Asdiscussedabove,discretisationinspaceofthescalarfield 31 class(arrvec_t) :: this ψ(x,y) into its ψ grid representation requires floating-point 32 integer, intent(in) :: n [i,j] 33 arraycontainers. Inturn,discretisationintimerequiresacon- 34 this%length = n tainer class for storing sequences of such arrays, i.e. {ψ[n], 35 allocate(this%at( -n : n-1 )) 36 allocate(this%arrs( 0 : n-1 )) ψ[n+1]}. Similarly the componentsof the vector fieldC~ are in 37 end subroutine facta{C[x],C[y]}arraysequence. 38 39 subroutine arrvec_init(this, n, i, j) Using an additional array dimension to represent the se- 40 class(arrvec_t), target :: this quence elements is not considered for two reasons. First, the 41 integer, intent(in) :: n 42 integer, intent(in) :: i(2), j(2) 43 3NumPy is a Python package for scientific computing offering support for multi-dimensional arrays and a library of numerical algorithms, see 5Boostisafreeandopen-sourcecollectionofpeer-reviewedC++libraries http://numpy.org/ availableathttp://www.boost.org/.SeveralpartsofBoosthavebeeninte- 4numpy.seterr()isnotsupportedinPyPyasofversion1.9 gratedintoorinspirednewadditionstotheC++standard. 3 4445 atlhlios%caatt(e(nt)h%ips=%>artrhsi(sn%)a%rar(si((n1)) : i(2), j(1) : j(2) )) Cve[[icx+]t1o/2,rj]c,oCm[[iyp,]jo−1/n2]enantsdsCur[[iry,o]j+u1/n2]dtiongdψepict.thHeowgreivdevr,alfuraecstioofntahleinC~- 46 this%at(n - this%length)%p => this%arrs(n) [i,j] 47 end subroutine dexing does not have a built-in counterpart in any of the em- 48 end module ployedprogramminglanguages.Adesiredsyntaxwouldtrans- latei−1/2toi−1andi+1/2toi. OOPoffersaconvenientwayto The arr_t type is defined solely for the purpose of overcom- implementsuchnotationbyoverloadingthe+and-operators ing the limitation of lack of an array-of-arraysconstruct, and forobjectsrepresentingarrayindices. its only memberfield is a two-dimensionalarray. An arrayof In the C++ implementation first a global instance h of an arr_tisusedhereinafterasacontainerforsequencesofarrays. empty structure hlf_t is defined, and then the plus and minus Thearrptr_ttypeisdefinedsolelyforthepurposeofover- operatorsforhlf_tandrng_tareoverloaded: coming Fortran’s limitation of not supporting allocatables of listing C.5 (C++) pointers. arrptr_t’ssingle memberfield is a pointerto an in- 19 struct hlf_t {} h; 20 stance of arr_t. Creating an allocatable of arrptr_t, instead 21 inline rng_t operator+(const rng_t &i, const hlf_t &) ofamulti-elementpointerofarr_t,ensuresautomaticmemory 22 { 23 return i; deallocation. 24 } Type arrptr_t is used to implement the from-the-end ad- 25 26 inline rng_t operator-(const rng_t &i, const hlf_t &) dressing of elements in arrvec_t. The array data is stored in 27 { thearrsmemberfield(oftypearr_t).Theatmemberfield(of 28 return i-1; 29 } type arrptr_t) stores pointers to the elements of arrs. at has double the length of arrs and is initialised in a cyclic manner Thisway, the arraysrepresentingvectorfield componentscan so that the -1 elementof at pointsto the last elementof arrs, be indexed using (i+h,j), (i-h,j) etc. where h represents the and so on. Assuming psi is an instance of arrptr_t, the (i,j) half. elementofthen-tharrayinpsimaybeaccessedwith In NumPy in order to preventcopying of array data during psi%at(n)%p%a(i,j). slicingoneneedstooperateontheso-calledarrayviews. Array Thector(n)methodinitialisesthecontainerforagivennum- viewsareobtainedwhenindexingthearrayswithobjectsofthe berofelementsn.Theinit(n,i,j)methodinitialisesthen-thele- Python’sbuilt-itslicetype(ortuplesofsuchobjectsincaseof mentofthecontainerwithanewlyallocated2Darrayspanning multi-dimensionalarrays).Pythonforbidsoverloadingofoper- indices i(1):i(2), and j(1):j(2)in the first, and last dimensions atorsofbuilt-intypessuchasslices, anddoesnotdefineaddi- respectively6. tion/subtractionoperatorsforsliceandintpairs.Consequently, a customlogichasto bedefinednotonlyforfractionalindex- 2.3. Staggeredgrid ing,butalsoforshiftingtheslicesbyintegerintervals(i±1).It isimplementedherebydeclaringashiftclasswiththeadequate operatoroverloads: listing P.3 (Python) 15 class shift(): ψ[i,j+1] 16 def __init__(self, plus, mnus): 17 self.plus = plus 18 self.mnus = mnus 19 def __radd__(self, arg): C[[ix−]1/2,j] C[[ix+]1/2,j] 222012 reaatrruggr..nsstttyaoprpet(++arssgee)ll(ff..pplluuss, ψ[i−1,j] ψ[i,j] 23 ) 24 def __rsub__(self, arg): 25 return type(arg)( C[y] 26 arg.start - self.mnus, [i,j−1/2] 27 arg.stop - self.mnus 28 ) andtwoinstancesofittorepresentunityandhalfinexpressions likei+one,i+hlf,whereiisaninstanceofslice7: Figure1:AschematicoftheArakawa-Cgrid. listing P.4 (Python) 29 one = shift(1,1) 30 hlf = shift(0,1) The so-called Arakawa-C staggered grid [20] depicted in Figure 1 is a natural choice for MPDATA. As a consequence, InFortranfractionalarrayindexingisobtainedthroughdef- the discretised representations of the ψ scalar field, and each initionandinstantiationofanobjectrepresentingthehalf,and componentof theC~ =~v· ∆t vectorfield in eq. (1) are defined havingappropriateoperatoroverloads: ∆x over different grid point locations. In mathematical notation thiscanbeindicatedbyusageoffractionalindices,e.g.C[x] , [i−1/2,j] 7One could argue that not using an own implementation of a slice- representingclassinNumPyisadesignflaw–beingabletomodifybehaviour ofahypotheticalnumpy.sliceclassthroughinheritancewouldallowtoimple- 6InFortran,whenanarrayispassedasafunctionargumentitsbaseislocally mentthesamebehaviourasobtainedinlistingP.3withouttheneedtorepresent settounity,regardlessofthesettingatthecallerscope. theunityasaseparateobject 4 listing F.3 (Fortran) 95 integer :: return(2) 49 module arakawa_c_m 96 50 implicit none 97 return = (/ r(1) - n, r(2) + n /) 51 98 end function 52 type :: half_t 99 53 end type 100 function ext_h(r, h) result (return) 54 101 integer, intent(in) :: r(2) 55 type(half_t) :: h 102 type(half_t), intent(in) :: h 56 103 integer :: return(2) 57 interface operator (+) 104 58 module procedure ph 105 return = (/ r(1) - h, r(2) + h /) 59 end interface 106 end function 60 107 end module 61 interface operator (-) 62 module procedure mh 63 end interface Consequently, a range depicted by i± 1/2 may be expressed 64 65 contains in the codeasext(i, h). In allthreeimplementationsthe ext() 66 functionacceptthesecondargumenttobeanintegerora”half” 67 elemental function ph(i, h) result (return) 68 integer, intent(in) :: i (cf. section2.3). 69 type(half_t), intent(in) :: h 70 integer :: return 71 return = i 2.5. Arrayindexpermutations 72 end function Hereinafter,theπd symbolisusedtodenoteacyclicpermu- 73 a,b 74 elemental function mh(i, h) result (return) tation of an orderd of a set {a,b}. It is used to generalise the 75 integer, intent(in) :: i MPDATAformulæintomultipledimensionsusingthefollow- 76 type(half_t), intent(in) :: h 77 integer :: return ingnotation: 78 return = i - 1 79 end function 80 end module 1 ψ ≡ψ +ψ [i,j]+πd [i+1,j] [i,j+1] Xd=0 1,0 2.4. Haloregions TheMPDATAformulædefiningψ[n+1] asafunctionofψ[n] Blitz++ ships with the RectDomain class (aliased here as [i,j] [i,j] idx_t)forspecifyingarrayrangesinmultipledimensions. The (discussed in the following sections) feature terms such as πpermutationisimplementedinC++asafunctionpi()return- ψ . Onewayofassuringvalidityoftheseformulæonthe [i−1,j−1] inganinstanceofidx_t. Inordertoensurecompile-timeeval- edgesofthedomain(e.g. fori=0)istointroducetheso-called uation,thepermutationorderispassedviathetemplateparam- haloregionsurroundingthedomain.Themethodofpopulating eterd(notethedifferentorderofiandjargumentsinthetwo the halo region with data depends on the boundary condition templatespecialisations): type.Employmentofthehalo-regionlogicimpliesrepeatedus- ageofarrayrangeextensionsinthecodesuchasi{i±halo. listing C.7 (C++) 37 template<int d> An ext() function is defined in all three implementation, in 38 inline idx_t pi(const rng_t &i, const rng_t &j); ordertosimplifycodingofarrayrangeextensions: 39 40 template<> listing C.6 (C++) 41 inline idx_t pi<0>(const rng_t &i, const rng_t &j) 30 template<class n_t> 42 { 31 inline rng_t ext(const rng_t &r, const n_t &n) { 43 return idx_t({i,j}); 32 return rng_t( 44 }; 33 (r - n).first(), 45 34 (r + n).last() 46 template<> 35 ); 47 inline idx_t pi<1>(const rng_t &j, const rng_t &i) 36 } 48 { 49 return idx_t({i,j}); listing P.5 (Python) 50 }; 31 def ext(r, n): 32 if (type(n) == int) & (n == 1): 33 n = one NumPy uses tuples of slices for addressing multi- 34 return slice( dimensionalarraywithasingleobject.Therefore,thefollowing 35 (r - n).start, 36 (r + n).stop definitionoffunctionpi()sufficestorepresentπ: 37 ) listing P.6 (Python) listing F.4 (Fortran) 38 def pi(d, *idx): 81 module halo_m 39 return (idx[d], idx[d-1]) 82 use arakawa_c_m 83 implicit none In the Fortran implementation pi() returns a pointer to the 84 85 interface ext array elements specified by i and j interpreted as (i,j) or (j,i) 86 module procedure ext_n dependingonthevalueoftheargumentd. Inadditiontopi(),a 87 module procedure ext_h 88 end interface helperspan()functionreturningthelengthofoneofthevectors 89 passedasargumentisdefined: 90 contains 91 listing F.5 (Fortran) 92 function ext_n(r, n) result (return) 108 module pi_m 93 integer, intent(in) :: r(2) 109 use real_m 94 integer, intent(in) :: n 110 implicit none 5 111 contains 67 bcx(i, j, hlo), 112 function pi(d, arr, i, j) result(return) 68 bcy(j, i, hlo) 113 integer, intent(in) :: d 69 { 114 real(real_t), allocatable, target :: arr(:,:) 70 for (int l = 0; l < 2; ++l) 115 real(real_t), pointer :: return(:,:) 71 psi.push_back(new arr_t(ext(i, hlo), ext(j, hlo))); 116 integer, intent(in) :: i(2), j(2) 72 C.push_back(new arr_t(ext(i, h), ext(j, hlo))); 117 select case (d) 73 C.push_back(new arr_t(ext(i, hlo), ext(j, h))); 118 case (0) 74 } 119 return => arr( i(1) : i(2), j(1) : j(2) ) 75 120 case (1) 76 // accessor methods 121 return => arr( j(1) : j(2), i(1) : i(2) ) 77 arr_t state() { 122 end select 78 return psi[n](i,j).reindex({0,0}); 123 end function 79 } 124 80 125 pure function span(d, i, j) result(return) 81 arr_t courant(int d) 126 integer, intent(in) :: i(2), j(2) 82 { 127 integer, intent(in) :: d 83 return C[d]; 128 integer :: return 84 } 129 select case (d) 85 130 case (0) 86 // helper methods invoked by solve() 131 return = i(2) - i(1) + 1 87 virtual void advop() = 0; 132 case (1) 88 133 return = j(2) - j(1) + 1 89 void cycle() 134 end select 90 { 135 end function 91 n = (n + 1) % 2 - 2; 136 end module 92 } 93 94 // integration logic The span() function is used to shorten the declarations of ar- 95 void solve(const int nt) rays to be returnedfromfunctionsin the Fortranimplementa- 96 { 97 for (int t = 0; t < nt; ++t) tion(seelistingsF.11andF.17–F.20). 98 { It isworthnotingherethatthe C++ implementationofpi() 99 bcx.fill_halos(psi[n], ext(j, hlo)); 100 bcy.fill_halos(psi[n], ext(i, hlo)); isbranchlessthankstoemploymentoftemplatespecialisation. 101 advop(); With Fortran one needs to rely on compiler optimisations to 102 cycle(); 103 } eliminate the conditional expression within the pi() that de- 104 } pendsonvalueofdwhichisalwaysknownatcompiletime. 105 }; Thesolverstructureisanabstractdefinition(containingapure 2.6. Prototypesolver virtualmethod)requiringitsdescendantstoimplementatleast The tasks to be handled by a prototype advection equation theadvop()methodwhichisexpectedtofillpsi[n+1]withan solverproposedhereinare: updated(advected)valuesofpsi[n]. Thetwotemplateparame- (i) storingarraysrepresentingtheψandC~ fieldsandanyre- tersbcx_tandbcy_tallowthesolvertooperatewithanykind ofboundaryconditionstructuresthatfulfiltherequirementsim- quiredhousekeepingdata, pliedbythecallstothemethodsofbcxandbcy,respectively. (ii) allocatinganddeallocatingtherequiredmemory, Thedonor-cellandMPDATAschemesbothrequireonlythe previousstate of an advectedfield in order to advancethe so- (iii) providingaccesstothesolverstate, lution. Consequently, memory for two time levels (ψ[n] and ψ[n+1]) is allocated in the constructor. The sizes of the arrays (iv) performing the integration by invoking the advection- representingthetwotimelevelsofψaredefinedbythedomain operatorandboundary-conditionhandlingroutines. size(nx×ny)plusthehaloregion. Thesizeofthehaloregion In the following C++ definition of the solver structure, task is an argumentof the constructor. The cycle()methodis used (i) is represented with the definition of the structure member toswapthetimelevelswithoutcopyinganydata. fields;task(ii)issplitbetweenthesolver’sconstructorandthe The arrays representingthe C[x] andC[y] componentsofC~, destructors of arrvec_t; task (iii) is handled by the accessor require(nx+1)×nyandnx×(ny+1)elements,respectively(be- methods;task(iv)ishandledwithinthesolvemethod: inglaidoutontheArakawa-Cstaggeredgrid). listing C.8 (C++) PythondefinitionofthesolverclassfollowscloselytheC++ 51 template<class bcx_t, class bcy_t> structuredefinition: 52 struct solver 53 { listing P.7 (Python) 54 // member fields 40 class solver(object): 55 arrvec_t psi, C; 41 # ctor-like method 56 int n, hlo; 42 def __init__(self, bcx, bcy, nx, ny, hlo): 57 rng_t i, j; 43 self.n = 0 58 bcx_t bcx; 44 self.hlo = hlo 59 bcy_t bcy; 45 self.i = slice(hlo, nx + hlo) 60 46 self.j = slice(hlo, ny + hlo) 61 // ctor 47 62 solver(int nx, int ny, int hlo) : 48 self.bcx = bcx(0, self.i, hlo) 63 hlo(hlo), 49 self.bcy = bcy(1, self.j, hlo) 64 n(0), 50 65 i(0, nx-1), 51 self.psi = ( 66 j(0, ny-1), 52 numpy.empty(( 6 53 ext(self.i, self.hlo).stop, 139 implicit none 54 ext(self.j, self.hlo).stop 140 55 ), real_t), 141 type, abstract :: bcd_t 56 numpy.empty(( 142 contains 57 ext(self.i, self.hlo).stop, 143 procedure(bcd_fill_halos), deferred :: fill_halos 58 ext(self.j, self.hlo).stop 144 procedure(bcd_init), deferred :: init 59 ), real_t) 145 end type 60 ) 146 61 147 abstract interface 62 self.C = ( 148 subroutine bcd_fill_halos(this, a, j) 63 numpy.empty(( 149 import :: bcd_t, real_t 64 ext(self.i, hlf).stop, 150 class(bcd_t ) :: this 65 ext(self.j, self.hlo).stop 151 real(real_t), allocatable :: a(:,:) 66 ), real_t), 152 integer :: j(2) 67 numpy.empty(( 153 end subroutine 68 ext(self.i, self.hlo).stop, 154 69 ext(self.j, hlf).stop 155 subroutine bcd_init(this, d, n, hlo) 70 ), real_t) 156 import :: bcd_t 71 ) 157 class(bcd_t) :: this 72 158 integer :: d, n, hlo 73 # accessor methods 159 end subroutine 74 def state(self): 160 end interface 75 return self.psi[self.n][self.i, self.j] 161 end module 76 77 # helper methods invoked by solve() 78 def courant(self,d): Having defined the abstract type for boundary-condition ob- 79 return self.C[d][:] jects, a definition of a solver class following closely the C++ 80 81 def cycle(self): andPythoncounterpartsmaybeprovided: 82 self.n = (self.n + 1) % 2 - 2 listing F.7 (Fortran) 83 162 module solver_m 84 # integration logic 163 use arrvec_m 85 def solve(self, nt): 164 use bcd_m 86 for t in range(nt): 165 use arakawa_c_m 87 self.bcx.fill_halos( 166 use halo_m 88 self.psi[self.n], ext(self.j, self.hlo) 167 implicit none 89 ) 168 90 self.bcy.fill_halos( 169 type, abstract :: solver_t 91 self.psi[self.n], ext(self.i, self.hlo) 170 class(arrvec_t), allocatable :: psi, C 92 ) 171 integer :: n, hlo 93 self.advop() 172 integer :: i(2), j(2) 94 self.cycle() 173 class(bcd_t), pointer :: bcx, bcy 95 174 contains 175 procedure :: solve => solver_solve The key difference stems from the fact that, unlike Blitz++, 176 procedure :: state => solver_state 177 procedure :: courant => solver_courant NumPydoesnotallowanarrayto havearbitraryindexbase– 178 procedure :: cycle => solver_cycle inNumPythefirstelementisalwaysaddressedwith0. Conse- 179 procedure(solver_advop), deferred :: advop 180 end type quently,whileinC++(andFortran)thecomputationaldomain 181 ischosentostartat(i=0,j=0)andhenceapartofthehalore- 182 abstract interface 183 subroutine solver_advop(this) gion to have negativeindices, in Pythonthe halo regionstarts 184 import solver_t at(0,0)8. However,sincethewholehalologicishiddenwithin 185 class(solver_t), target :: this 186 end subroutine thesolver,suchdetailsarenotexposedtotheuser. Thebcxand 187 end interface bcyboundary-conditionspecificationsarepassedtothesolver 188 189 contains throughconstructor-like__init__()methodasopposedtotem- 190 plateparametersinC++. 191 subroutine solver_ctor(this, bcx, bcy, nx, ny, hlo) 192 use arakawa_c_m The above C++ and Python prototype solvers in principle 193 use halo_m allow to operatewith anyboundaryconditionobjectsthatim- 194 class(solver_t) :: this 195 class(bcd_t), intent(in), target :: bcx, bcy plement methods called from within the solver. This require- 196 integer, intent(in) :: nx, ny, hlo ment is checked at compile-time in the case of C++, and at 197 198 this%n = 0 run-time in the case of Python. In order to obtain an analo- 199 this%hlo = hlo gous behaviour with Fortran, it is required to define, prior to 200 this%bcx => bcx 201 this%bcy => bcy definition of a solvertype, an abstracttype with deferredpro- 202 cedureshavingabstractinterfaces[sic!,seeTable2.1in21,for 203 this%i = (/ 0, nx - 1 /) 204 this%j = (/ 0, ny - 1 /) asummaryofapproximatecorrespondenceofOOPnomencla- 205 turebetweenFortranandC++]: 206 call bcx%init(0, nx, hlo) 207 call bcy%init(1, ny, hlo) listing F.6 (Fortran) 113378 moudsueleabrcrdv_emc_m 222001890 aclallolctahtie(st%phsiis%%pcstio)r(2) 211 block 8Thereasontoallowthedomaintobeginatanarbitraryindexismainlyto 212 integer :: n easedebuggingincasethecodewouldbeusedinparallelcomputationsusing 213 do n=0, 1 214 call this%psi%init( & domaindecompositionwhereeachsubdomaincouldhaveitsownindexbase 215 n, ext(this%i, hlo), ext(this%j, hlo) & correspondingtothelocationwithinthecomputationaldomain 216 ) 7 217 end do 124 void fill_halos(const arr_t &a, const rng_t &j) 218 end block 125 { 219 126 a(pi<d>(left_halo, j)) = a(pi<d>(rght_edge, j)); 220 allocate(this%C) 127 a(pi<d>(rght_halo, j)) = a(pi<d>(left_edge, j)); 221 call this%C%ctor(2) 128 } 222 call this%C%init( & 129 }; 223 0, ext(this%i, h), ext(this%j, hlo) & 224 ) 225 call this%C%init( & Ashintedbythememberfieldnames,thefill_halos()meth- 226 1, ext(this%i, hlo), ext(this%j, h) & ods fill the left/righthalo regionswith data from the right/left 227 ) 228 end subroutine edges of the domain. Thanks to employment of the function 229 pi() describedin section 2.5 the same code may be applied in 230 function solver_state(this) result (return) 231 class(solver_t) :: this anydimension(herebeingatemplateparameter). 232 real(real_t), pointer :: return(:,:) ListingsP.8andF.8containthePythonandFortrancounter- 233 return => this%psi%at(this%n)%p%a( & 234 this%i(1) : this%i(2), & partstolistingC.9. 235 this%j(1) : this%j(2) & 223367 en)d function 2.8. Donor-cellformulæ(C++) 238 MPDATA is an iterative algorithm in which each iteration 239 function solver_courant(this, d) result (return) 240 class(solver_t) :: this takestheformoftheso-calleddonor-cellformula(whichitself 241 integer :: d isafirst-orderadvectionscheme). 242 real(real_t), pointer :: return(:,:) 243 return => this%C%at(d)%p%a MPDATA and donor-cell are explicit forward-in-time algo- 244 end function rithms–theyallowtopredictψ[n+1] asafunctionofψ[n] where 245 246 subroutine solver_cycle(this) n and n + 1 denote two adjacent time levels. The donor-cell 247 class(solver_t) :: this schememaybewrittenas[eq. 2in7]: 248 this%n = mod(this%n + 1 + 2, 2) - 2 249 end subroutine 250 N−1 251 subroutine solver_solve(this, nt) ψ[n+1]=ψ[n] − F ψ[n] ,ψ[n] ,C[d] 225523 cilnatsesg(esr,olivnetre_ntt)(:i:n)th:i:snt [i,j] [i,j] Xd=0(cid:18) (cid:20) [i,j] [i,j]+πd1,0 [i,j]+πd1/2,0(cid:21) (2) 254 integer :: t 255 −F ψ[n] ,ψ[n] ,C[d] 225567 doctal=l t0,hinst%-b1cx%fill_halos( & " [i,j]+πd−1,0 [i,j] [i,j]+πd−1/2,0#! 258 this%psi%at(this%n)%p%a, ext(this%j, this%hlo) & where N is the number of dimensions, and F is the so-called 259 ) 260 call this%bcy%fill_halos( & fluxfunction[7,eq.3]: 261 this%psi%at(this%n)%p%a, ext(this%i, this%hlo) & 262 ) F(ψ ,ψ ,C)=max(C,0)·ψ +min(C,0)·ψ 263 call this%advop() L R L R 264 call this%cycle() C+|C| C−|C| (3) 265 end do = ·ψ + ·ψ 266 end subroutine 2 L 2 R 267 end module ThefluxfunctiontakesthefollowingforminC++: listing C.10 (C++) 2.7. Periodicboundaries(C++) 130 template<class T1, class T2, class T3> 131 inline auto F( From this point, only C++ implementation is explained in 132 const T1 &psi_l, const T2 &psi_r, const T3 &C themaintext. ThePythonandFortranimplementationsarein- 133 ) return_macro( 134 ( cludedinappendicesPandF. 135 (C + abs(C)) * psi_l + Thesolverdefinitiondescribedinsection2.6requiresagiven 136 (C - abs(C)) * psi_r 137 ) / 2 boundaryconditionobjecttoimplementafill_halos()method. 138 ) AnimplementationofperiodicboundaryconditionsinC++is providedinthefollowinglisting: Equation2 is split into the termsunder the summation(effec- listing C.9 (C++) tivelythe1-dimensionaldonor-cellformula): 106 template<int d> listing C.11 (C++) 107 struct cyclic 139 template<int d> 108 { 140 inline auto donorcell( 109 // member fields 141 const arr_t &psi, const arr_t &C, 110 rng_t left_halo, rght_halo; 142 const rng_t &i, const rng_t &j 111 rng_t left_edge, rght_edge;; 143 ) return_macro( 112 144 F( 113 // ctor 145 psi(pi<d>(i, j)), 114 cyclic( 146 psi(pi<d>(i+1, j)), 115 const rng_t &i, const rng_t &j, int hlo 147 C(pi<d>(i+h, j)) 116 ) : 148 ) - 117 left_halo(i.first()-hlo, i.first()-1), 149 F( 118 rght_edge(i.last()-hlo+1, i.last() ), 150 psi(pi<d>(i-1, j)), 119 rght_halo(i.last()+1, i.last()+hlo ), 151 psi(pi<d>(i, j)), 120 left_edge(i.first(), i.first()+hlo-1) 152 C(pi<d>(i-h, j)) 121 {} 153 ) 122 154 ) 123 // method invoked by the solver 8 andtheactualtwo-dimensionaldonor-cellformula: For positive-definite ψ, the A and B terms take the following form9: listing C.12 (C++) ψ −ψ 115556 vociodndsotnaorrrcveelcl__top&(psi, const int n, A[[di,]j]= ψ[i,j]+πd1,0+ψ[i,j] (6) 157 const arrvec_t &C, [i,j]+πd [i,j] 158 const rng_t &i, const rng_t &j 1,0 115690 ) p{si[n+1](i,j) = psi[n](i,j) B[d] = 1ψ[i,j]+πd1,1+ψ[i,j]+πd0,1−ψ[i,j]+πd1,−1−ψ[i,j]+πd0,−1 (7) 116612 -- ddoonnoorrcceellll<<01>>((ppssii[[nn]],, CC[[01]],, ij,, ji)); [i,j] 2ψ[i,j]+πd1,1+ψ[i,j]+πd0,1+ψ[i,j]+πd1,−1+ψ[i,j]+πd0,−1 163 } If the denominator in equations 6 or 7 equals zero for a given i and j, the corresponding A and B are set to zero [i,j] [i,j] ListingsP.9-P11andF.9-F.13containthePythonandFortran whatmaybeconvenientlyrepresentedwiththewhereconstruct counterpartstolistingsC.12-C.15. (availableinallthreeconsideredlanguages): listing C.14 (C++) 179 template<class nom_t, class den_t> 2.9. Donor-cellsolver(C++) 180 inline auto mpdata_frac( 181 const nom_t &nom, const den_t &den 182 ) return_macro( Asmentionedintheprevioussection,thedonor-cellformula 183 where(den > 0, nom / den, 0) constitutesanadvectionscheme,hencewemayuseittocreate 184 ) asolver_donorcellimplementationoftheabstractsolverclass: TheAtermdefinedinequation6takesthefollowingform: listing C.13 (C++) 164 template<class bcx_t, class bcy_t> listing C.15 (C++) 165 struct solver_donorcell : solver<bcx_t, bcy_t> 185 template<int d> 166 { 186 inline auto mpdata_A(const arr_t &psi, 167 solver_donorcell(int nx, int ny) : 187 const rng_t &i, const rng_t &j 168 solver<bcx_t, bcy_t>(nx, ny, 1) 188 ) return_macro( 169 {} 189 mpdata_frac( 170 190 psi(pi<d>(i+1, j)) - psi(pi<d>(i,j)), 171 void advop() 191 psi(pi<d>(i+1, j)) + psi(pi<d>(i,j)) 172 { 192 ) 173 donorcell_op( 193 ) 174 this->psi, this->n, this->C, 175 this->i, this->j TheBtermdefinedinequation7takesthefollowingform: 176 ); 177 } listing C.16 (C++) 178 }; 194 template<int d> 195 inline auto mpdata_B(const arr_t &psi, 196 const rng_t &i, const rng_t &j The above definition is given as an example only. In the fol- 197 ) return_macro( 198 mpdata_frac( lowing sections an MPDATA solver of the same structure is 199 psi(pi<d>(i+1, j+1)) + psi(pi<d>(i, j+1)) - defined. 200 psi(pi<d>(i+1, j-1)) - psi(pi<d>(i, j-1)), 201 psi(pi<d>(i+1, j+1)) + psi(pi<d>(i, j+1)) + ListingsP.12andF.14containthePythonandFortrancoun- 202 psi(pi<d>(i+1, j-1)) + psi(pi<d>(i, j-1)) terpartstolistingC.16. 203 ) / 2 204 ) 2.10. MPDATAformulæ(C++) Equation5takesthefollowingform: listing C.17 (C++) MPDATA introduces corrective steps to the algorithm de- 205 template<int d> 206 inline auto mpdata_C_bar( fined by equation 2 and 3. Each corrective step is a donor- 207 const arr_t &C, cellstep(eq.2)withtheCourantnumberfieldscorresponding 208 const rng_t &i, 209 const rng_t &j to the MPDATA antidiffusive velocities of the followingform 210 ) return_macro( [eqs13,14in7]: 211 ( 212 C(pi<d>(i+1, j+h)) + C(pi<d>(i, j+h)) + 213 C(pi<d>(i+1, j-h)) + C(pi<d>(i, j-h)) C[′i[,dj]]+πd1/2,0=(cid:12)(cid:12)C[[id,]j]+πd1/2,0(cid:12)(cid:12)·(cid:20)1−(cid:12)(cid:12)C[[id,]j]+πd1/2,0(cid:12)(cid:12)(cid:21)·A[[di,]j](ψ) 221145 ) ) / 4 (cid:12)(cid:12) N (cid:12)(cid:12) (cid:12)(cid:12) (cid:12)(cid:12) (4) −(cid:12) C[(cid:12)d] (cid:12)·C[q] (cid:12)·B[d] (ψ) Equation4takethefollowingform: q=X0,q,d [i,j]+πd1/2,0 [i,j]+πd1/2,0 [i,j] 216 template<int d> listing C.18 (C++) 217 inline auto mpdata_C_adf( whereψandCrepresentvaluesfromthepreviousiterationand 218 const arr_t &psi, 219 const rng_t &i, const rng_t &j, where: 220 const arrvec_t &C 221 ) return_macro( C[q] = 1 · C[q] +C[q] + 222 abs(C[d](pi<d>(i+h, j))) [i,j]+πd1/2,0 4 (cid:18) [i,j]+πd1,1/2 [i,j]+πd0,1/2 (5) C[q] +C[q] 9Sinceψ ≥0,|A|≤ 1and|B|≤ 1. SeeSmolarkiewicz[11,Sec. 4.2]for [i,j]+πd1,−1/2 [i,j]+πd0,−1/2! descriptionofadaptationoftheformulæforadvectionoffieldsofvariablesign 9 223 * (1 - abs(C[d](pi<d>(i+h, j)))) 296 donorcell_op( 224 * mpdata_A<d>(psi, i, j) 297 this->psi, this->n, C_corr, this->i, this->j 225 - C[d](pi<d>(i+h, j)) 298 ); 226 * mpdata_C_bar<d>(C[d-1], i, j) 299 } 227 * mpdata_B<d>(psi, i, j) 300 } 228 ) 301 } 302 }; ListingsP.13-P.17andF.15-F.21containthePythonandFor- The array of sequences of temporary arrays tmp allocated in trancounterpartstolistingC.16-C.22. theconstructorisusedtostoretheantidiffusivevelocitiesfrom 2.11. MPDATAsolver(C++) thepresentandoptionallyprevioustimestep(ifusingmorethan twoiterations). An MPDATA solver may be now constructed by inheriting Theadvop()methodcontrollstheMPDATAiterationswithin fromsolverclasswiththefollowingdefinitioninC++: one timestep. The first (step = 0) iteration of MPDATA is an listing C.19 (C++) 229 template<int n_iters, class bcx_t, class bcy_t> unmodifieddonor-cellstep(comparelistingC.15).Subsequent 230 struct solver_mpdata : solver<bcx_t, bcy_t> iterations begin with calculation of the antidiffusive Courant 231 { 232 // member fields fields using formula 4. In order to calculate values spanning 223334 arrnrgv_etci_mt,tjmmp;[2]; an (i−1⁄2 ... i+1⁄2) range using a formula for C[i+1/2,...] only, the formula is evaluated using extended index ranges im and jm. 235 236 // ctor In the second (step=1) iteration the uncorrectedCourant field 237 solver_mpdata(int nx, int ny) : 238 solver<bcx_t, bcy_t>(nx, ny, 1), (C_unco) points to the original C field, and the antidiffusive 239 im(this->i.first() - 1, this->i.last()), Courant field is written into C_corr which points to tmp[1]. 240 jm(this->j.first() - 1, this->j.last()) 241 { In the third (step=2) iteration C_unco points to tmp[1] while 242 int n_tmp = n_iters > 2 ? 2 : 1; C_corrpointstotmp[0]. Insubsequentiterationstmp[0]and 243 for (int n = 0; n < n_tmp; ++n) 244 { tmp[1]arealternatelyswapped. 245 tmp[n].push_back(new arr_t( ListingsP.18andF.22containthePythonandFortrancoun- 246 this->C[0].domain()[0], this->C[0].domain()[1]) 247 ); terpartstolistingC.23. 248 tmp[n].push_back(new arr_t( 224590 );this->C[1].domain()[0], this->C[1].domain()[1]) 2.12. Usageexample(C++) 251 } The following listing providesan example of how the MP- 252 } 253 DATAsolverdefinedinsection2.11maybeusedtogetherwith 254 // method invoked by the solver thecyclicboundaryconditionsdefinedinsection2.7. Intheex- 255 void advop() 256 { ample a Gaussian signal is advected in a 2D domain defined 257 for (int step = 0; step < n_iters; ++step) over a grid of 24×24 cells. The program first plots the ini- 258 { 259 if (step == 0) tial condition, then performs the integration for 75 timesteps 260 donorcell_op( with three different settings of the number of iterations used 261 this->psi, this->n, this->C, this->i, this->j 262 ); in MPDATA. The velocity field is constant in time and space 263 else (althoughitisnotassumedinthepresentedimplementations). 264 { 265 this->cycle(); Thesignalshapeattheendofeachsimulationisplottedaswell. 266 this->bcx.fill_halos( Plottingisdonewiththehelpofthegnuplot-iostreamlibrary10. 267 this->psi[this->n], ext(this->j, this->hlo) 268 ); The resultant plot is presented herein as Figure 2. The top 269 this->bcy.fill_halos( paneldepictstheinitialcondition.Thethreeotherpanelsshow 270 this->psi[this->n], ext(this->i, this->hlo) 271 ); a snapshot of the field after 75 timesteps. The donor-cell so- 272 lution is characterisedby strongestnumericaldiffusion result- 273 // choosing input/output for antidiff C 274 const arrvec_t inginsignificantdropinthesignalamplitude. Thesignalsad- 275 &C_unco = (step == 1) vectedusingMPDATA showsmallernumericaldiffusionwith 276 ? this->C 277 : (step % 2) the solution obtained with more iterations preserving the sig- 278 ? tmp[1] // odd steps nal altitude more accurately. In all of the simulations the sig- 279 : tmp[0], // even steps 280 &C_corr = (step % 2) nalmaintainsitspositivedefiniteness. Thedomainperiodicity 281 ? tmp[0] // odd steps is apparentin the plots as the maximumof the signalafter 75 282 : tmp[1]; // even steps 283 timestepsislocatednearthedomainwalls. 284 // calculating the antidiffusive C Listings P.19 and F.23-F.24 contain the Python and Fortran 285 C_corr[0](im+h, this->j) = mpdata_C_adf<0>( 286 this->psi[this->n], im, this->j, C_unco counterpartstolisting C.24(with theset-up andplottinglogic 287 ); omitted). 288 this->bcy.fill_halos(C_corr[0], ext(this->i,h)); 289 290 C_corr[1](this->i, jm+h) = mpdata_C_adf<1>( 291 this->psi[this->n], jm, this->i, C_unco 10gnuplot-iostreamisaheader-onlyC++libraryallowinggnuplottobecon- 292 ); trolled from C++, see http://stahlke.org/dan/gnuplot-iostream/. 293 this->bcx.fill_halos(C_corr[1], ext(this->j,h)); Gnuplot is a portable command-line driven graphing utility, see 294 295 // donor-cell step http://gnuplot.info/ 10

Description:
tion equation solver written in C++11 (ISO/IEC 14882:2011),. Python [13] and Fortran 2008 (ISO/IEC 1539-1:2010). In the following section we [16] M. Pilgrim, Dive Into Python, Apress, 2004. [17] A. Markus, Modern Fortran in
See more

The list of books you might like

Most books are stored in the elastic cloud where traffic is expensive. For this reason, we have a limit on daily download.