Líneas de actuación
RED DE OFICINAS 2006 2005 Oficinas operativas 495
6.3 Calidad en el servicio (4.12) (PR3)
A very important subsidiary to a good software design is a good testing framework. A test suite which is simple to execute, fast and easy to expand not only allows to verify that the current status of a piece of software is correct, but it also allows to verify that all future changes do not break anything. This naturally includes a potential adoption of the design towards future requirements. A good test suite generally aids with any code refactoring, since all changes can be performed in a sequence of many small steps, verifying the correctness of the software on the way.
For this reason molsturm comes with an extensive test framework with roughly four types of tests. Firstly there are unit tests, which test the functionality of a single function or code unit in a couple of hard-coded examples. Further we have functionality
tests, which test a larger portion of code and are meant to ensure that the results of
molsturmagree between different versions. Thirdly the reference tests compare the
results of molsturm to other quantum-chemistry packages. Especially in lazyten we furthermore make use of yet another type of tests, namely so-called property-based
tests. This testing technique uses the expected properties of a code unit to randomly
generate test cases and to verify the result as well. On failure the generated test cases are simplified until the most simple, failing test case is found. This is very helpful to reduce the human aspect in testing and finding the actual issue during debugging.
Next to combining various different types of tests, it is very important that running the tests by itself is hassle-free. Only in this way they are actually used. In molsturm we therefore employ the continuous integration service offered free of charge by Travis CI GmbH for open source projects: Whenever a new commit is made to our github
2
That is not the parameters provided by the user, but the post-processed parameters which were actually used by the lower layers, amended, for example, by default values.
7.3. EXAMPLES 161 repository, a set of virtual machines start automatically in order to checkout and build
molsturmcompletely from scratch in a few typical build configurations. Afterwards the
test suite is automatically executed and all output generated by the test suite displayed. In this way even people, who are unfamiliar with all details of molsturm can test their changes thoroughly, which encourages code contributions. Additionally this gives us
molsturmdevelopers the chance to easily make sure that no untested code enters the
stable source branch, since only if the continuous integration testing passes, a commit to the stable branch is allowed.
One might argue that such a continuous integration system only achieves its purpose if users committing new code furthermore write the accompanying tests to check its validity. For this reason we try to make it simple to add new tests by providing tooling to automate this process as well. For example in order to add a new reference test for a new feature or a corner case, where a bug was discovered, one only needs to add a small configuration file to a special directory. Afterwards one only needs to call a python script, which picks up the configuration file, reads the desired test case and calls the external third-party reference quantum-chemistry program (currently ORCA [130]) to compute the required data. From the next time the molsturm test suite is executed, this additional reference test will be part of the test suite as well.
Another way we employ to make sure that most of molsturm’s code is indeed tested is a measure called coverage analysis. Roughly speaking this method inserts special checkpoints during the compilation of a program, which allow to retrace which code paths have been executed and which have not. In combination with our automatic continuous integration builds, we use this to check automatically which parts of the code have been touched when the test suite was executed, i.e. which parts of molsturm are covered by the test suite. Ideally one would keep test coverage close to 100%, implying that literally every line of molsturm was tested. In practice in most modules of molsturm we currently achieve between 80% and 90% coverage. Notice that coverage analysis is more powerful than just detecting untested code paths. For example it allows to find hot spots, i.e. parts of the code executed very often, or dead code, which is never executed. Similar to a pass of the continuous integration builds, we currently enforce that molsturm’s coverage may not decrease by more that 0.5% each time code is merged into the stable branch. This makes sure that most of molsturm’s code really gets tested each time new features are added.
7.3
Examples
In this section we present a few examples, which demonstrate how the python interface of molsturm could be combined with existing features of python in order to analyse results or to extend the capabilities of molsturm. In all cases shown the computations are done using contracted Gaussian basis sets. It should be noted, however, that due to the basis-function-independent nature of molsturm the procedures outlined in the scripts could be easily used with other types of basis functions as well.
162 CHAPTER 7. THE MOLSTURM METHOD-DEVELOPMENT FRAMEWORK 1 from m a t p l o t l i b import pyplot as plt
2 import m o l s t u r m
3 import m o l s t u r m . posthf
4 import numpy as np
5 from scipy . o p t i m i z e import c u r v e _ f i t
6 7
8 def c o m p u t e _ c u r v e ( atom , basis_args , c o n v _ t o l =1 e -6 ,
9 zrange =(0.65 , 7.15) , 10 n _ p o i n t s =30) : 11 d i s t a n c e s = np . l i n s p a c e ( zrange [1] , zrange [0] , 12 n _ p o i n t s ) 13 e n e r g i e s = np . e m p t y _ l i k e ( d i s t a n c e s ) 14 p r e v i o u s _ h f = None 15 16 for i , z in e n u m e r a t e( d i s t a n c e s ) : 17 system = m o l s t u r m . System (
18 atoms =[ atom , atom ] ,
19 coords =[(0 , 0 , 0) , (0 , 0 , z ) ] ,
20 )
21
22 # Run a UHF and s u b s e q u e n t UMP2 c a l c u l a t i o n
23 # using the basis p a r a m e t e r s . If a p r e v i o u s
24 # result exists ( i . e . this is not the first
25 # HF c a l c u l a t i o n we do along the curve )
26 # use it as a guess .
27 guess = " random " if not p r e v i o u s _ h f \
28 else p r e v i o u s _ h f
29 state = m o l s t u r m . h a r t r e e _ f o c k (
30 system , c o n v _ t o l = conv_tol , guess = guess ,
31 r e s t r i c t e d = False , ** b a s i s _ a r g s 32 ) 33 mp2 = m o l s t u r m . posthf . mp2 ( state ) 34 e n e r g i e s [ i ] = mp2 [" e n e r g y _ g r o u n d _ s t a t e "] 35 p r e v i o u s _ h f = state 36 return distances , e n e r g i e s 37 38
39 def p l o t _ m o r s e _ f i t ( dist , ene ) :
40 # First fit Morse p o t e n t i a l :
41 def morse (x , de , a , xeq , off ) :
42 return de * (1 - np . exp ( - a * ( x - xeq ) ) ) **2 + off
43 popt , pcov = c u r v e _ f i t ( morse , dist , ene )
44
45 # Plot data and Morse using 100 s a m p l i n g points :
46 x = np . l i n s p a c e ( np .min( dist ) , np .max( dist ) , 100)
47 plt . plot ( dist , ene , " + ", label =" UMP2 ")
48 plt . plot (x , morse (x , * popt ) ,
49 label =" Morse p o t e n t i a l ")
50
51 plt . xlabel (" Bond d i s t a n c e in Bohr ")
52 plt . ylabel (" Energy in H a r t r e e ") 53 plt . legend () 54 55 56 def main () : 57 # C o m p u t e the H2 d i s s o c i a t i o n using a p a r t i c u l a r
58 # basis type , b a c k e n d and basis set :
59 b a s i s _ a r g s = { 60 " b a s i s _ t y p e ": " g a u s s i a n ", 61 " b a c k e n d ": " libint ", 62 " b a s i s _ s e t _ n a m e ": " def2 - svp " 63 } 64 distances , e n e r g i e s = c o m p u t e _ c u r v e (" H ", b a s i s _ a r g s ) 65 66 p l o t _ m o r s e _ f i t ( distances , e n e r g i e s ) 67 plt . show () 68 69 70 if _ _ n a m e _ _ == " _ _ m a i n _ _ ": 71 main ()
Figure 7.2: Script for computing a H2dissociation curve and fitting a Morse potential
to it. The decision about the basis function type, the integral back end as well as the basis set are only made in line 64, where the compute_curve is called with the chosen set of parameters.
7.3. EXAMPLES 163
Figure 7.3: Plot resulting from computing the H2 dissociation curve in a def2-SVP
basis set [251] at UMP2 level, employing the script of figure 7.2. Shown are both the UMP2 energy values as well as the fitted Morse potential.