There are no silver bullet for compromise. Where should we compromise and where should not? → I will introduce my experience from development of my MD code.
Investors Award) Algorithm Classical Moleculer Dynamics method with short-range interaction Support only Lennard-Jones potential with cutoff Target Phase transition between gas and liquid Universality of gas-liquid phase transition (Equilibrium state) Waiting time of a bubble nucleation (Quasi static State) Multi-bubble nuclei (Non-equilibrium phenomena)
SPaSM, MODYLAS, etc) We can write papers with using above codes. If I were a physisist, I used the above codes. But I am a programmer! I want to write a paper with using MY CODE. I want to challenge the state of the art by MY CODE.
Hybrid for ver 2.0 Size: ~5000 lines URL: https://github.com/kaityo256/mdacp Design Policy • Pursuing both ease of development and efficiency → If the two interfere, take efficiency • Do not consider use by others → Publishing source codes for developers, rather than users • Less dependency on external libraries
Variables MDUnit (Thread) MDManager (Process) Variables MDUnit (Thread) Variables MDUnit (Thread) A Project Execute an appropriate project among pre-registered main function Actual main function Communication
to some function • Subclass can be created by inheritance Inheritance • Subclass can be created by inheritance • Usually used with virtual functions Virtual Function • Override the behavior of superclass • The method to be called is determined at runtime
TextForm::Repaint CheckBox::Repaint Component::Repaint The Form manages components and it does not know the details. The form calls Component::Repaint when repaint is necessary The derived methods, such as Button::Repaint, are called. No need to recompile Form class even if the types of components increases
dependent on each other (Tight Coupling) • Independent of each other (Loose Coupling) Tightly coupled modules: Local modifications spread Loosely coupled modules: Modifications affect locally Loose coupling is better ... but I often adopted designs which do not minimize the coupling.
Temperature SystemSize=32 Tempreature=1.5 <PARAM name=SystemSize>32</PARAM> <PARAM name=Temperature>1.5</PARAM> Coupling Tight Loose Command line arguments Ordered numbers in a file Key=Value format in a file XML or other formats Adopted
※ Other formats, such as TOML, look good... XML seems to be overspec for me. • XML format requires external library. • XML is hard to be modified by hand. Implementation • Parameter class (parameter.cc/h) • String hash (std::map) • "Key=Value" format
to executable 2. Receive the filename in main, and pass it to manager class 3. Manager class creates an instance of Parameter class from it 4. Read values via the instance of Parameter class Retrieving Values Get a value via string hash double Parameter::GetDouble(string key) double Parameter::GetDoubleDef(string key, double default) Special Key "Mode" Input file must include the special key "Mode" → It will be used for managements of projects
difference from benchmark codes) Coupling Tight Loose Rewrite main function and recompile Switch statement Singleton Pattern Create a library Support some script languages Adopted
polygon nparticle bodies units lj dimension 2 atom_style body nparticle 2 6 read_data data.body velocity all create 1.44 87287 loop geom pair_style body 5.0 pair_coeff * * 1.0 1.0 neighbor 0.3 bin fix 1 all nve/body fix 2 all enforce2d #compute 1 all body/local type 1 2 3 #dump 1 all local 100 dump.body index c_1[1] c_1[2] c_1[3] c_1[4] thermo 500 run 10000 github: lammps/examples/body/in.body User friendly Very hard for a programmer
2. Register itself to Project Manager at its constructor ProjectA “ProjectA” ProjectB “ProjectB” ProjectC “ProjectC” ProjectManager hashkey Pointer to itself Regester 3. Find the project by the key, and call Project::Run method Mode=ProjectC Run ProjectA “ProjectA” ProjectB “ProjectB” ProjectManager ProjectC “ProjectC”
Recompiling is unnecessary when a project is added. Implementation Implement ProjectManager class as singleton All projects are subclasses of the Project class. Project::Run is the actual main function (user implements it). Pros/Cons Easy for implementation, relatively loose coupling Inconvenient for other users ※ Maybe I am an only user...
MPI requires them for initialization • Parameter class requires it to obtain an input filename. How should I pass them? Tight Loose Main func passes the arguments to manager class. Main func creates two instances of classes which require the arguments and pass two instances to manager class. Design Changed plan B plan A
Parameter management class (Parameter) Pass "argc" and "argv" to their constructors Pass MPIInfo and Parameter to Calculation Management class argc,argv main MPIInfo Parameter argc,argv argc,argv MDManager I adopted Plan A for Ver 1.0.
manager class. • Manager class creates MPIInfo and Parameter by using them. argc,argv main MPI_Init Parameter argc,argv argc,argv MDManager int main(int argc, char *argv[]){ MDManager mdm(argc, argv); } I think Plan A is more beautiful, but I adopted Plan B for Ver 2.0.
class? Don't you think that the design is cleaner if you divide it? Q A Because I thought there was not much profit to divide... • Instances of MPIInfo and Parameter share their lifetime with MDManager. • Should MDManager know its rank? or MPIInfo should? • Simple main function is preferable.
better. • Different architectures require different optimizations. • I want to deal with various types of interactions. Implement Force calculation directly Implement virtual function for each architecture Implement virtual function for each interaction Adopted
automatically chosen. • The most beautiful design. • Desperately slow Virtual functions for each architecture ForceCalc_SPARC64 is inherited from ForceCalc, etc... →Highly inefficient codes Since some compiler cannot apply IPO for calling virtual functions. ※ IPO (Interprocedural optimization)
other. MDUnit knows who lives in the neighborhood. Updates divided reagion (Local Information) Communicate to each other (Grobal Information) I adopted Plan A for Ver 1.0.
(Responsible for the "map") MDUnit has MPIInfo. Loose coupling Plan B MDUnit MDUnit MDUnit MDUnit MDManager MDManager knows map. Tight coupling I adopted Plan A for Ver 1.0, but changed to Plan B for Ver 2.0.
you think that the design is cleaner if you divide it? Q A Because I thought there was not much profit to divide... MDManager calls MPI_Init in its constructor and MPI_Finalize in its destructor. So MPI-related functions are not separated ...
is difficult to achieve performance and maintainability simultaneously. • Rewrite codes if you feel uncomfortable. Parallel programs are cumbersome • I tried to hide the parallel structure. • But understanding parallel structure is mandatory for something complicated...