-
Notifications
You must be signed in to change notification settings - Fork 1
/
Model.cpp
130 lines (102 loc) · 4.02 KB
/
Model.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
//call kernel 1000 times then run timer then divide by 1000 so that get average time
//then make kernel a functoin
//then change below to python, add in PK's code; call function
double EllipsoidModel :: operator()(double qx, double qy) {
EllipsoidParameters dp;
// Fill parameter array
dp.scale = scale();
dp.radius_a = radius_a();
dp.radius_b = radius_b();
dp.sldEll = sldEll();
dp.sldSolv = sldSolv();
dp.background = 0.0;
dp.axis_theta = axis_theta();
dp.axis_phi = axis_phi();
// Get the dispersion points for the radius_a
vector<WeightPoint> weights_rad_a;
radius_a.get_weights(weights_rad_a); //just give weight rather than have this
// Get the dispersion points for the radius_b
vector<WeightPoint> weights_rad_b;
radius_b.get_weights(weights_rad_b);
// Get angular averaging for theta
vector<WeightPoint> weights_theta;
axis_theta.get_weights(weights_theta);
// Get angular averaging for phi
vector<WeightPoint> weights_phi;
axis_phi.get_weights(weights_phi);
// Perform the computation, with all weight points
double sum = 0.0;
double norm = 0.0;
double norm_vol = 0.0;
double vol = 0.0;
double pi = 4.0*atan(1.0);
// Loop over radius weight points
for(size_t i=0; i<weights_rad_a.size(); i++) {
dp.radius_a = weights_rad_a[i].value;
// Loop over length weight points
for(size_t j=0; j<weights_rad_b.size(); j++) {
dp.radius_b = weights_rad_b[j].value;
// Average over theta distribution
for(size_t k=0; k<weights_theta.size(); k++) {
dp.axis_theta = weights_theta[k].value;
// Average over phi distribution
for(size_t l=0; l<weights_phi.size(); l++) {
dp.axis_phi = weights_phi[l].value;
//Un-normalize by volume
double _ptvalue = weights_rad_a[i].weight
* weights_rad_b[j].weight
* weights_theta[k].weight
* weights_phi[l].weight
* ellipsoid_analytical_2DXY(&dp, qx, qy) //make sure calls kernel
* pow(weights_rad_b[j].value,2) * weights_rad_a[i].value;
if (weights_theta.size()>1) {
_ptvalue *= fabs(cos(weights_theta[k].value*pi/180.0));
}
sum += _ptvalue;
//Find average volume
vol += weights_rad_a[i].weight
* weights_rad_b[j].weight
* pow(weights_rad_b[j].value,2) * weights_rad_a[i].value;
//Find norm for volume
norm_vol += weights_rad_a[i].weight
* weights_rad_b[j].weight;
norm += weights_rad_a[i].weight
* weights_rad_b[j].weight
* weights_theta[k].weight
* weights_phi[l].weight;
}
}
}
}
// Averaging in theta needs an extra normalization
// factor to account for the sin(theta) term in the
// integration (see documentation).
if (weights_theta.size()>1) norm = norm / asin(1.0);
if (vol != 0.0 && norm_vol != 0.0) {
//Re-normalize by avg volume
sum = sum/(vol/norm_vol);}
return sum/norm + background();
}
class GaussianDispersion(object):
def __init__(self, npts=35, width=0, nsigmas=3):
self.type = 'gaussian'
self.npts = npts
self.width = width
self.nsigmas = nsigmas
def get_pars(self):
return self.__dict__
def get_weights(self, center, min=-inf, max=+inf, relative=False):
"""
*center* is the center of the distribution
*min*,*max* are the min, max allowed values
*relative* is True if the width is relative to the center instead of absolute
For polydispersity use relative. For orientation parameters use absolute.
"""
npts, width, nsigmas = self.npts, self.width, self.nsigmas
sigma = width * center if relative else width
if sigma == 0:
return numpy.array([center, 1.], 'd')
x = center + numpy.linspace(-nsigmas * sigma, +nsigmas * sigma, npts)
x = x[(x >= min) & (x <= max)]
px = numpy.exp((x-center)**2 / (-2.0 * sigma * sigma))
return x,px