4 september 2013 Miniprojekt 1 (5) Beräkningsvetenskap DV Institutionen för informationsteknologi Beräkningsvetenskap Besöksadress: Polacksbacken, hus 2 Lägerhyddsvägen 2 Postadress: Box 337 751 05 Uppsala Telefon: 018 471 0000 (växel) Telefax: 018 52 30 49 Hemsida: http://www.it.uu.se Department of Information Technology Scientific Computing Visiting address: Polacksbacken, bldg 2 Lägerhyddsvägen 2 Postal address: Box 337 SE-751 05 Uppsala SWEDEN Telephone: +46 18 471 0000 (switch) Telefax: +46 18 52 30 49 Web page: http://www.it.uu.se Miniprojekt: MEX och molekyldynamik Efter att ha ägnat en hel del tid i The Newton s Mill Restoration Project med att simulera vatten och det tryck som det åstadkommer på makronivå, vill din chef nu att ni också undersöker vad som händer på molekylnivå. En kollega till dig har redan skrivit ett MATLAB-program för molekyldynamik, men beräkningarna är svåra att uttrycka på ett sätt som gör att de utförs effektivt i MATLAB, och simuleringarna går för långsamt. Din kollega har åtgärdat problemet genom att skriva en effektivare version av programmet i C, men din chef vill kunna använda MATLAB för att köra simuleringarna och rita ut resultatet. Du har nu därför fått i uppdrag att koppla ihop de två programmen, så att de effektiva beräkningsrutinerna skrivna i C anropas från MATLAB. Bakgrund I programmet du fått beskrivs interaktionen mellan molekyler med hjälp av Lennard Jones-potentialen, som visas i Figur 1. Modellen tar hänsyn till två molekyler i taget, och figuren visar att om avståndet mellan två partiklar är mindre än 1 längdenhet stöter de bort varandra, och om avståndet är större än 1 längdenhet dras de mot varandra, med en kraft som avtar när avståndet ökar. Detta är en enkel modell som beskriver hur gaser som till exempel argon beter sig, men som passar mindre väl för att modellera vattenmolekyler. Mer specifikt beskriver modellen så kallade inerta gaser, det vill säga gaser som inte reagerar kemiskt, utan beter sig som hårda sfärer som dras mot varandra av van der Waals-krafter. Potential 2.5 2.0 1.5 1.0 0.5 0.0 0.5 1.0 1.5 0.0 0.5 1.0 1.5 2.0 2.5 Avstånd Figur 1: Lennard Jones-potentialen Simuleringen består av två steg. I det första steget beräknas alla krafter mellan molekylerna, och i det andra steget flyttas molekylerna i enlighet med de krafter som räknades ut. Efter ett antal tidssteg så ritas molekylerna ut.
2 (5) MATLABs MEX-gränssnitt Vi ska använda MATLABs MEX-gränssnitt för att anropa C-program. MEX-filer är vanliga C-filer, som måste inkludera mex.h och ska ha en funktion mexfunction med signatur som här under: #include "mex.h" void mexfunction(int nlhs, mxarray * plhs[], int nrhs, const mxarray * prhs[]); När man kör en MEX-fil från MATLAB anropas denna funktion. Parametern nlhs (number of left-hand sides) anger hur många utdata som MATLAB förväntar sig. Dessa utdata ska placeras i arrayen plhs (pointer to left-hand sides). Parametern nrhs (number of right-hand sides) anger hur många argument funktionen anropades med, och de finns i arrayen prhs (pointer to right-hand sides). Om man har en har en MEX-fil mymex och anropar den från MATLAB genom att skriva [a, b] = mymex(c, d, e); så kommer alltså mexfunction i mymex.c att anropas med nlhs satt till 2 och nrhs satt till 3. Variablerna c, d och e kommer ligga i prhs[0], prhs[1] och prhs[2], och när funktionen returnerar ska utdata ha lagrats i plhs[0] och plhs[1], som kommer sparas i variablerna a och b. Om MEX-funktionen anropas med mymex(c, d, e), det vill säga utan att ange någon variabel att placera returvärdet i, så kommer nlhs vara 0. Det är dock fortfarande tillåtet att sätta ett returvärde i plhs[0]. Detta returvärde kommer då hamna i variabeln ans. För att överföra data mellan MATLAB och C används strukturen mxarray. Alla variabler i MATLAB hanteras som om de vore matriser där elementen är doubles. Rad- och kolumnvektorer hanteras alltså som en matris där antalet kolumner eller rader är 1, och skalärer hanteras som matriser av storlek 1 1. Sist i denna projektbeskrivning finns en kort sammanfattning av de funktioner vi kommer behöva. Mer utförlig dokumentation finns i MATLABs inbyggda hjälp, under MATLAB User s Guide C/C++ and Fortran API Reference. Del 1 Uppvärmning Skriva ut text Som ett första steg för att komma igång med MEX-filer ska vi skriva en MEXfunktion som bara skriver ut en hälsning på skärmen. Skapa en MEX-fil som heter mexhello.c. Den ska inkludera mex.h och innehålla en funktion mexfunction, som beskrivet här ovanför. Inuti själva funktionen mexfunction ska det bara vara ett anrop till funktionen mexprintf för att skriva ut en hälsning. Man använder mexprintf på samma sätt som printf (se sista sidan), men MAT- LAB tillhandahåller egna versioner av många standardfunktioner i C som man ska använda i stället. Kompilera i MATLAB genom att skriva >> mex mexhello.c och testkör genom att i MATLAB skriva
3 (5) >> mexhello Om allt fungerar ska MATLAB då skriva ut hälsningen. Ta emot och skicka skalärer Nästa steg är att skicka data till och från MATLAB. Skriv ett program mexsquare.c som tar in en skalär och returnerar värdet i kvadrat. Funktionen mexfunction ska i detta fall läsa in det första argumentet, det vill säga prhs[0], som en skalär med hjälp av mxgetscalar, beräkna kvadraten av värdet och sedan returnera det nya värdet genom att skapa en ny skalär med mxcreatedoublescalar och sätta plhs[0] till denna. Kompilera och testkör så här: >> mex mexsquare.c >> mexsquare(4) ans = 16 I det här fallet kontrollerar vi inte att man verkligen skickade med ett argument till funktionen mexsquare, och inte heller att argumentet verkligen var en skalär. Anropar man mexsquare utan argument kommer MATLAB att krasha. Testa gärna. Därför är det viktigt att man faktiskt kontrollerar att varje indata finns och har rätt datatyp. Tips: Titta på exemplet mexample.c på kurshemsidan om det är oklart hur funktionerna används. Skicka och ta emot matriser Som en sista uppvärmning ska vi ta emot en matris som argument, och returnera en ny matris, där varje element är dubblerat. Skapa en ny MEX-funktion som heter mexdblmatrix.c. Antag att en matris har skickats in som parameter och hamnat i prhs[0]. Använd mxgetm och mxgetn för att läsa av antalet rader respektive kolumner i den inskickade matrisen och spara dessa i variabler m och n. Skapa sedan en ny matris genom att anropa mxcreatedoublematrix(m, n, mxreal). Använd mxgetpr för att få en pekare till elementen i vardera matris. Loopa sedan igenom de m n elementen, läs från inmatrisen, dubblera värdet, och skriv det i utmatrisen. Se slutligen till att utmatrisen returneras genom att sätta plhs[0]. Bifoga koden för mexhello.c, mexsquare.c och mexdblmatrix.c i din projektrapport. Del 2 För att undvika krasher måste man kontrollera att det indata som skickats stämmer med vad man försöker ta emot. Titta på exemplet mexample.c som finns att ladda hem på kurshemsidan. Detta är en funktion som tar in en skalär och en matris, och returnerar matrisen transponerad och multiplicerad med skalären, samt det största elementet i utmatrisen.
4 (5) Börja med att ladda hem funktionen från hemsidan, och se efter hur den är skriven. Kompilera och testkör funktionen så här: >> mex mexample.c >> [a, b] = mexample(2, [1 2 3 ; 4 5 6]) MATLAB ska då svara b = 2 8 4 10 6 12 Beskriv i din projektrapport hur indata kontrolleras i mexample.c. Del 3 Nu när du testat MEX-interfacet är det dags att ta sig an koden för molekyldynamik. Din uppgift är att anropa beräkningsrutinerna skrivna i C från MATLAB. Börja med att ladda hem programmen mdsim.m och mdsimmex.c från kurshemsidan. MATLAB-programmet mdsim.m simulerar partiklar så som det beskrivs i bakgrund-avsnittet, och mdsimmex.c innehåller en version av samma simulering som är skriven i C. Läs igenom programmen så att du förstår vad de gör. Testkör MATLAB-implementationen genom att till exempel köra mdsim(10); Siffran 10 anger att 10 10 partiklar simuleras. Efter var 20:e tidssteg så kommer partiklarna ritas ut, och den tid det tog för att utföra de 20 tidsstegen skrivs ut. Skriv om mdsimmex.c så att den kan anropas från MATLAB. Var uppmärksam på att C-programmet och MATLAB-programmet förväntar sig att data ligger lagrat på olika sätt i minnet. Ändra sedan i mdsim.m så att den anropar C-koden i stället för att använda den motsvarande MATLAB-koden. Ta hjälp av exemplet mexample.c för att se hur MEX-gränssnittet används. Behåll tidtagningen i mdsim.m och jämför skillnaden i hastighet mellan C- och MATLAB-versionen. Bifoga de ändrade filerna mdsimmex.c och mdsim.m, och jämför skillnaden i hastighet i din rapport. Del 4 (inte obligatorisk) Översätt koden från del 3 i miniprojektet Dammen vid Newton s Mill till C, och anropa den via MEX i stället. Använd tic och toc och jämför hur lång tid de två olika versionerna tar.
5 (5) Referens: MEX-funktioner Funktion: int mexprintf(const char *message,...); Användning: Skriver ut en text på skärmen. Funktion: size t mxgetm(const mxarray *pm); Användning: Returnerar antalet rader i matrisen pm. Funktion: size t mxgetn(const mxarray *pm); Användning: Returnerar antalet kolumner i matrisen pm. Funktion: bool mxisdouble(const mxarray *pm); Användning: Returnerar true om matrisen pm har element av typen double. Funktion: double *mxgetpr(const mxarray *pm); Användning: Returnerar en pekare till elementen i matrisen pm. Endast giltig om mxisdouble returnerar true. Elementen är lagrade i en ordning som kallas column-major och innebär att matrisen ( ) 1 2 3 4 5 6 ligger sparad i minnet som [1, 4, 2, 5, 3, 6]. Detta skiljer sig från hur man brukar ordna elementen i C. Funktion: double mxgetscalar(const mxarray *pm); Användning: Smidigare sätt att läsa en skalär. Endast giltig om man har testat datatyp och storlek på matrisen. Funktion: void mexerrmsgtxt(const char *); Användning: Skriver ut ett felmeddelande i MATLAB och avbryter körningen av MEX-filen. Funktion: void *mxmalloc(size t size); void *mxcalloc(size t num, size t size); Användning: Allokerar minne. För att tillåta MATLAB att hantera allt minne och undvika att läcka minne om körningen skulle avbrytas bör man använda MATLABs rutiner för minnesallokering i stället för vanliga malloc(). Funktion: void mxfree(void *); Användning: Frigör minne som tidigare allokerats med mxmalloc eller mxcalloc. Minne kan frigöras manuellt om det inte längre behövs, men annars är det MATLABs ansvar att frigöra minnet. Funktion: mxarray *mxcreatedoublematrix(size t m, size t n, mxcomplexity complexflag); Användning: Skapa en matris med m rader och n kolumner. Sätt complexflag till att vara mxreal för att indikera att vi vill ha reella tal och inte komplexa tal i matrisen. Funktion: mxarray *mxcreatedoublescalar(double value); Användning: Skapar en skalär. Ett smidigare sätt att skapa en skalär än att använda mxcreatedoublematrix. Funktion: mxarray *void mxdestroyarray(mxarray *pm); Användning: Frigör variabler som tidigare skapats med någon av funktionerna mxcreatedoublematrix eller mxcreatedoublescalar.