source: sasview/sansmodels/test/validate_2D_model.py @ 7bfc888

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.1.1release-4.1.2release-4.2.2release_4.0.1ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since 7bfc888 was 18e250c, checked in by Gervaise Alina <gervyh@…>, 13 years ago

move test to sansmodels top level

  • Property mode set to 100644
File size: 7.6 KB
RevLine 
[ae3ce4e]1#!/usr/bin/env python
2"""
3    Class to validate a given 2D model by averaging it
4    and comparing to 1D prediction.
5   
6    The equation used for averaging is:
7   
8        (integral dphi from 0 to 2pi)(integral dtheta from 0 to pi)
9             p(theta, phi) I(q) sin(theta) dtheta
10             
11        = (1/N_phi) (1/N_theta) (pi/2) (sum over N_phi) (sum over N_theta)
12             p(theta_i, phi_i) I(q) sin(theta_i)
13             
14    where p(theta, phi) is the probability distribution normalized to 4pi.
15    In the current case, we put p(theta, phi) = 1.
16   
17    The normalization factor results from:
18          2pi/N_phi      for the phi sum
19        x pi/N_theta     for the theta sum
20        x 1/(4pi)        because p is normalized to 4pi
21        --------------
22        = (1/N_phi) (1/N_theta) (pi/2)
23   
24    Note: Ellipsoid
25        Averaging the 2D ellipsoid scattering intensity give a slightly
26        different output than the 1D function from the IGOR library
27        at hight Q (Q>0.3). This is due to the way the IGOR library
28        averages, taking only 76 points in alpha, the angle between
29        the axis of the ellipsoid and the q vector.
30       
31    Note: Core-shell and sphere models are symmetric around
32        all axes and don't need to be tested in the following way.
33"""
34import sys, math
35from sans.models.SphereModel import SphereModel
36from sans.models.CylinderModel import CylinderModel
37from sans.models.EllipsoidModel import EllipsoidModel
38from sans.models.CoreShellCylinderModel import CoreShellCylinderModel
39
40
41class Validate2D:
42    """
43        Class to validate a given 2D model by averaging it
44        and comparing to 1D prediction.
45    """
46   
47    def __init__(self):
48        """ Initialization """
49        # Precision for the result comparison
50        self.precision = 0.000001
51        # Flag for end result
52        self.passed = True
53               
54    def __call__(self, model_class=CylinderModel, points = 101):
55        """
56            Perform test and produce output file
57            @param model_class: python class of the model to test
58        """
59        print "Averaging %s" % model_class.__name__
60        passed = True
61       
62        npts =points
63        model = model_class()
64        #model.setParam('scale', 1.0)
65        #model.setParam('contrast', 1.0)
66       
67        theta_label = 'cyl_theta'
68        if not model.params.has_key(theta_label):
69            theta_label = 'axis_theta'
70           
71        phi_label = 'cyl_phi'
72        if not model.params.has_key(phi_label):
73            phi_label = 'axis_phi'
74       
75        output_f = open("%s_avg.txt" % model.__class__.__name__,'w')   
76        output_f.write("<q_average> <2d_average> <1d_average>\n")
77           
78        for i_q in range(1, 30):
[da987e1]79            q = 0.025 *i_q
[ae3ce4e]80            sum = 0.0
81            for i_theta in range(npts):
[da987e1]82                theta = 180.0 / npts * i_theta
[ae3ce4e]83               
84                model.setParam(theta_label, theta)
85               
86                for j in range(npts):
[96656e3]87                    model.setParam(phi_label, 180.0 * 2.0 / npts * j)
[ae3ce4e]88                    if str(model.run([q, 0])).count("INF")>0:                       
[96656e3]89                        print "ERROR", q, theta, 180.0 * 2.0 / npts * j
90                    sum += math.sin(theta*math.pi/180.0)*model.run([q, 0])
[ae3ce4e]91                    #sum += model.run([q, 0])
92           
[da987e1]93            value = sum/npts/npts*math.pi/2.0
[ae3ce4e]94            ana = model.run(q)
[da987e1]95            if q<0.25 and (value-ana)/ana>0.05:
[ae3ce4e]96                passed = False
97            output_f.write("%10g %10g %10g\n" % (q, value, ana))
98            print "Q=%g: %10g %10g %10g %10g" % (q, value, ana, value-ana, value/ana)
99       
100        output_f.close()
101        return passed
102   
103    def average(self, model_class=CylinderModel, points = 101):
104        """
105            Perform test and produce output file
106            @param model_class: python class of the model to test
107        """
108        print "Averaging %s" % model_class.__name__
109        passed = True
110       
111        npts =points
112        model = model_class()
113        model.setParam('scale', 1.0)
114        model.setParam('contrast', 1.0)
115       
116        theta_label = 'cyl_theta'
117        if not model.params.has_key(theta_label):
118            theta_label = 'axis_theta'
119           
120        phi_label = 'cyl_phi'
121        if not model.params.has_key(phi_label):
122            phi_label = 'axis_phi'
123       
124        output_f = open("%s_avg.txt" % model.__class__.__name__,'w')   
125        output_f.write("<q_average> <2d_average> <1d_average>\n")
126           
127        for i_q in range(1, 30):
128            q = 0.025*i_q
129            sum = 0.0
[96656e3]130            theta = 90.0
[ae3ce4e]131           
132            model.setParam(theta_label, theta)
133           
134            for j in range(npts):
[96656e3]135                model.setParam(phi_label, 180.0 / 2.0 / npts * j)
[ae3ce4e]136                if str(model.run([q, 0])).count("INF")>0:                       
[96656e3]137                    print "ERROR", q, theta, 180.0 * 2.0 / npts * j
138                #sum += math.sin(theta*math.pi/180.0)*model.run([q, 0])
[ae3ce4e]139                sum += math.sin(math.pi / 2.0 / npts * j)*model.run([q, 0])
140                #sum += model.run([q, 0])
141           
142            value = sum/npts
143            ana = model.run(q)
[da987e1]144            if q<0.25 and (value-ana)/ana>0.05:
[ae3ce4e]145                passed = False
146            output_f.write("%10g %10g %10g\n" % (q, value, ana))
147            print "Q=%g: %10g %10g %10g %10g" % (q, value, ana, value-ana, value/ana)
148       
149        output_f.close()
150        return passed
151       
152    def test_non_oriented(self, model_class=SphereModel, points = 101):
153        """
154            Perform test and produce output file
155            @param model_class: python class of the model to test
156        """
157        print "Averaging %s" % model_class.__name__
158        passed = True
159       
160        npts =points
161        model = model_class()
162        #model.setParam('scale', 1.0)
163        #model.setParam('contrast', 1.0)
164       
165        output_f = open("%s_avg.txt" % model.__class__.__name__,'w')   
166        output_f.write("<q_average> <2d_average> <1d_average>\n")
167           
168        for i_q in range(1, 30):
169            q = 0.025*i_q
170            sum = 0.0
171            for i_theta in range(npts):
[96656e3]172                theta = 180.0/npts*i_theta
[ae3ce4e]173               
174                for j in range(npts):
175                    if str(model.run([q, 0])).count("INF")>0:                       
[96656e3]176                        print "ERROR", q, theta, 180.0 * 2.0 / npts * j
177                    sum += math.sin(theta*math.pi/180.0)*model.run([q, 0])
[ae3ce4e]178           
[da987e1]179            value = sum/npts/npts*math.pi/2.0
[ae3ce4e]180            ana = model.run(q)
[da987e1]181            if q<0.25 and (value-ana)/ana>0.05:
[ae3ce4e]182                passed = False
183            output_f.write("%10g %10g %10g\n" % (q, value, ana))
184            print "Q=%g: %10g %10g %10g %10g" % (q, value, ana, value-ana, value/ana)
185       
186        output_f.close()
187        return passed
188           
189       
190if __name__ == '__main__':
191    validator = Validate2D()
192    cyl_passed = validator(CylinderModel, points=201)
193    ell_passed = validator(EllipsoidModel, points=501)
194    cylcorehsell_passed = validator(CoreShellCylinderModel, points=201) 
195    sph_passed = validator.test_non_oriented(SphereModel, points=211) 
196       
197    print ""
198    print "Model             Passed"
199    print "Cylinder          %s" % cyl_passed
200    print "Ellipsoid         %s" % ell_passed
201    print "Core-shell cyl    %s" % cylcorehsell_passed
202    print "Sphere            %s" % sph_passed
203       
204       
205 
206   
Note: See TracBrowser for help on using the repository browser.