Changeset b8080e1 in sasview for src/sas/sascalc/calculator
- Timestamp:
- Aug 29, 2018 10:01:23 AM (6 years ago)
- Branches:
- ESS_GUI, ESS_GUI_batch_fitting, ESS_GUI_bumps_abstraction, ESS_GUI_iss1116, ESS_GUI_iss879, ESS_GUI_opencl, ESS_GUI_ordering, ESS_GUI_sync_sascalc
- Children:
- 9463ca2
- Parents:
- ce30949
- git-author:
- Piotr Rozyczko <rozyczko@…> (08/29/18 09:59:56)
- git-committer:
- Piotr Rozyczko <rozyczko@…> (08/29/18 10:01:23)
- Location:
- src/sas/sascalc/calculator
- Files:
-
- 7 edited
Legend:
- Unmodified
- Added
- Removed
-
src/sas/sascalc/calculator/c_extensions/librefl.c
rf54e82cf rb8080e1 7 7 #include <stdio.h> 8 8 #include <stdlib.h> 9 #if defined (_MSC_VER)9 #if defined _MSC_VER || defined __TINYCC__ 10 10 #define NEED_ERF 11 11 #endif … … 21 21 22 22 23 #ifdef _WIN32 23 #ifdef __TINYCC__ 24 # ifdef isnan 25 # undef isnan 26 # endif 27 # ifdef isfinite 28 # undef isfinite 29 # endif 30 # define isnan(x) (x != x) 31 # define isfinite(x) (x != INFINITY && x != -INFINITY) 32 #elif defined _WIN32 24 33 # include <float.h> 25 34 # if !defined __MINGW32__ || defined __NO_ISOCEXT … … 30 39 # define isinf(x) (!_finite(x) && !_isnan(x)) 31 40 # endif 32 # ifndef finite33 # define finite(x) _finite(x)41 # ifndef isfinite 42 # define isfinite(x) _finite(x) 34 43 # endif 35 44 # endif … … 84 93 double erf(double x) 85 94 { 86 if (! finite(x)) {95 if (!isfinite(x)) { 87 96 if (isnan(x)) return x; /* erf(NaN) = NaN */ 88 97 return (x>0 ? 1.0 : -1.0); /* erf(+-inf) = +-1.0 */ … … 94 103 double erfc(double x) 95 104 { 96 if (! finite(x)) {105 if (!isfinite(x)) { 97 106 if (isnan(x)) return x; /* erfc(NaN) = NaN */ 98 107 return (x>0 ? 0.0 : 2.0); /* erfc(+-inf) = 0.0, 2.0 */ -
src/sas/sascalc/calculator/c_extensions/sld2i.c
rf54e82cf rb8080e1 25 25 * @param s_theta: angle (from x-axis) of the up spin in degree 26 26 */ 27 void initGenI(GenI* this, int npix, double* x, double* y, double* z, double* sldn,27 void initGenI(GenI* this, int is_avg, int npix, double* x, double* y, double* z, double* sldn, 28 28 double* mx, double* my, double* mz, double* voli, 29 29 double in_spin, double out_spin, 30 30 double s_theta) { 31 this->is_avg = is_avg; 31 32 this->n_pix = npix; 32 33 this->x_val = x; … … 63 64 Cplx temp_fi; 64 65 66 double count = 0.0; 65 67 int i, j; 66 67 double count = 0.0;68 //check if this computation is for averaging69 68 70 69 cassign(&iqr, 0.0, 0.0); … … 78 77 79 78 // Loop over q-values and multiply apply matrix 79 80 //printf("npoints: %d, npix: %d\n", npoints, this->n_pix); 80 81 for(i=0; i<npoints; i++){ 81 82 //I_out[i] = 0.0; … … 86 87 //printf("i: %d\n", i); 87 88 //q = sqrt(qx[i]*qx[i] + qy[i]*qy[i]); // + qz[i]*qz[i]); 89 88 90 for(j=0; j<this->n_pix; j++){ 89 91 if (this->sldn_val[j]!=0.0 … … 143 145 I_out[i] *= (1.0E+8 / count); //in cm (unit) / number; //to be multiplied by vol_pix 144 146 } 145 //printf ("count = %d %g %g %g %g\n", count, sldn_val[0],mx_val[0], my_val[0],mz_val[0]);147 //printf("count = %d %g %g %g %g\n", count, this->sldn_val[0],this->mx_val[0], this->my_val[0], this->mz_val[0]); 146 148 } 147 149 /** … … 154 156 // Assumes that q doesn't have qz component and sld_n is all real 155 157 //double Pi = 4.0*atan(1.0); 156 int is_sym = this->n_pix < 0;157 158 double qr = 0.0; 158 159 double sumj; 159 160 double sld_j = 0.0; 160 161 double count = 0.0; 161 int n_pix = is_sym ? -this->n_pix : this->n_pix; 162 int i, j, k; 163 162 164 //Assume that pixel volumes are given in vol_pix in A^3 unit 163 165 // Loop over q-values and multiply apply matrix 164 int i, j, k;165 166 for(i=0; i<npoints; i++){ 166 167 sumj =0.0; 167 for(j=0; j< n_pix; j++){168 for(j=0; j<this->n_pix; j++){ 168 169 //Isotropic: Assumes all slds are real (no magnetic) 169 170 //Also assumes there is no polarization: No dependency on spin 170 if ( is_sym== 1){171 if (this->is_avg == 1){ 171 172 // approximation for a spherical symmetric particle 172 173 qr = sqrt(this->x_val[j]*this->x_val[j]+this->y_val[j]*this->y_val[j]+this->z_val[j]*this->z_val[j])*q[i]; … … 182 183 //full calculation 183 184 //pragma omp parallel for 184 for(k=0; k< n_pix; k++){185 for(k=0; k<this->n_pix; k++){ 185 186 sld_j = this->sldn_val[j] * this->sldn_val[k] * this->vol_pix[j] * this->vol_pix[k]; 186 187 qr = (this->x_val[j]-this->x_val[k])*(this->x_val[j]-this->x_val[k])+ … … 201 202 } 202 203 I_out[i] = sumj; 203 if ( is_sym == 1){204 if (this->is_avg == 1) { 204 205 I_out[i] *= sumj; 205 206 } 206 207 I_out[i] *= (1.0E+8 / count); //in cm (unit) / number; //to be multiplied by vol_pix 207 208 } 208 //printf 209 //printf("count = %d %g %g %g %g\n", count, sldn_val[0],mx_val[0], my_val[0], mz_val[0]); 209 210 } -
src/sas/sascalc/calculator/c_extensions/sld2i.h
reca7c6f rb8080e1 10 10 typedef struct { 11 11 // vectors 12 int is_avg; 12 13 int n_pix; 13 14 double* x_val; … … 26 27 27 28 // Constructor 28 void initGenI(GenI*, int npix, double* x, double* y, double* z, double* sldn,29 double* mx, double* my, double* mz, double* voli,29 void initGenI(GenI*, int is_avg, int npix, double* x, double* y, double* z, 30 double* sldn, double* mx, double* my, double* mz, double* voli, 30 31 double in_spin, double out_spin, 31 32 double s_theta); -
src/sas/sascalc/calculator/c_extensions/sld2i_module.c
rf54e82cf rb8080e1 35 35 void 36 36 del_sld2i(PyObject *obj){ 37 #if PY_MAJOR_VERSION < 3 38 GenI* sld2i = (GenI *)obj; 39 #else 37 40 GenI* sld2i = (GenI *)(PyCapsule_GetPointer(obj, "GenI")); 41 #endif 38 42 PyMem_Free((void *)sld2i); 39 43 } … … 51 55 PyObject *mz_val_obj; 52 56 PyObject *vol_pix_obj; 53 Py_ssize_t n_x; 54 //PyObject rlimit_obj; 55 //PyObject npoints_obj; 56 //PyObject nrbins_obj; 57 //PyObject nphibins_obj; 58 int n_pix; 57 Py_ssize_t n_x, n_y, n_z, n_sld, n_mx, n_my, n_mz, n_vol_pix; 58 int is_avg; 59 59 double* x_val; 60 60 double* y_val; … … 68 68 double outspin; 69 69 double stheta; 70 GenI *sld2i; 71 72 if (!PyArg_ParseTuple(args, "iOOOOOOOOddd", &n_pix, &x_val_obj, &y_val_obj, &z_val_obj, &sldn_val_obj, &mx_val_obj, &my_val_obj, &mz_val_obj, &vol_pix_obj, &inspin, &outspin, &stheta)) return NULL; 73 OUTVECTOR(x_val_obj, x_val, n_x); 74 OUTVECTOR(y_val_obj, y_val, n_x); 75 OUTVECTOR(z_val_obj, z_val, n_x); 76 OUTVECTOR(sldn_val_obj, sldn_val, n_x); 77 OUTVECTOR(mx_val_obj, mx_val, n_x); 78 OUTVECTOR(my_val_obj, my_val, n_x); 79 OUTVECTOR(mz_val_obj, mz_val, n_x); 80 OUTVECTOR(vol_pix_obj, vol_pix, n_x); 81 sld2i = PyMem_Malloc(sizeof(GenI)); 70 PyObject *obj; 71 GenI* sld2i; 72 73 //printf("new GenI\n"); 74 if (!PyArg_ParseTuple(args, "iOOOOOOOOddd", &is_avg, &x_val_obj, &y_val_obj, &z_val_obj, &sldn_val_obj, &mx_val_obj, &my_val_obj, &mz_val_obj, &vol_pix_obj, &inspin, &outspin, &stheta)) return NULL; 75 INVECTOR(x_val_obj, x_val, n_x); 76 INVECTOR(y_val_obj, y_val, n_y); 77 INVECTOR(z_val_obj, z_val, n_z); 78 INVECTOR(sldn_val_obj, sldn_val, n_sld); 79 INVECTOR(mx_val_obj, mx_val, n_mx); 80 INVECTOR(my_val_obj, my_val, n_my); 81 INVECTOR(mz_val_obj, mz_val, n_mz); 82 INVECTOR(vol_pix_obj, vol_pix, n_vol_pix); 83 sld2i = PyMem_Malloc(sizeof(GenI)); 84 //printf("sldi:%p\n", sld2i); 82 85 if (sld2i != NULL) { 83 initGenI(sld2i, n_pix,x_val,y_val,z_val,sldn_val,mx_val,my_val,mz_val,vol_pix,inspin,outspin,stheta);86 initGenI(sld2i,is_avg,(int)n_x,x_val,y_val,z_val,sldn_val,mx_val,my_val,mz_val,vol_pix,inspin,outspin,stheta); 84 87 } 85 return PyCapsule_New(sld2i, "GenI", del_sld2i); 88 obj = PyCapsule_New(sld2i, "GenI", del_sld2i); 89 //printf("constructed %p\n", obj); 90 return obj; 86 91 } 87 92 … … 90 95 */ 91 96 PyObject * genicom_inputXY(PyObject *self, PyObject *args) { 92 int npoints;97 PyObject *gen_obj; 93 98 PyObject *qx_obj; 99 PyObject *qy_obj; 100 PyObject *I_out_obj; 101 Py_ssize_t n_qx, n_qy, n_out; 94 102 double *qx; 95 PyObject *qy_obj;96 103 double *qy; 97 PyObject *I_out_obj;98 Py_ssize_t n_out;99 104 double *I_out; 100 PyObject *gen_obj; 101 GenI *sld2i; 102 103 if (!PyArg_ParseTuple(args, "OiOOO", &gen_obj, &npoints, &qx_obj, &qy_obj, &I_out_obj)) return NULL; 104 OUTVECTOR(qx_obj, qx, n_out); 105 OUTVECTOR(qy_obj, qy, n_out); 105 GenI* sld2i; 106 107 //printf("in genicom_inputXY\n"); 108 if (!PyArg_ParseTuple(args, "OOOO", &gen_obj, &qx_obj, &qy_obj, &I_out_obj)) return NULL; 109 sld2i = (GenI *)PyCapsule_GetPointer(gen_obj, "GenI"); 110 INVECTOR(qx_obj, qx, n_qx); 111 INVECTOR(qy_obj, qy, n_qy); 106 112 OUTVECTOR(I_out_obj, I_out, n_out); 113 //printf("qx, qy, I_out: %d %d %d, %d %d %d\n", qx, qy, I_out, n_qx, n_qy, n_out); 107 114 108 115 // Sanity check 109 //if(n_in!=n_out) return Py_BuildValue("i",-1); 110 111 // Set the array pointers 112 sld2i = (GenI *)PyCapsule_GetPointer(gen_obj, "GenI"); 113 114 genicomXY(sld2i, npoints, qx, qy, I_out); 116 //if(n_q!=n_out) return Py_BuildValue("i",-1); 117 118 genicomXY(sld2i, (int)n_qx, qx, qy, I_out); 119 //printf("done calc\n"); 115 120 //return PyCObject_FromVoidPtr(s, del_genicom); 116 121 return Py_BuildValue("i",1); … … 121 126 */ 122 127 PyObject * genicom_input(PyObject *self, PyObject *args) { 123 int npoints;128 PyObject *gen_obj; 124 129 PyObject *q_obj; 130 PyObject *I_out_obj; 131 Py_ssize_t n_q, n_out; 125 132 double *q; 126 PyObject *I_out_obj;127 Py_ssize_t n_out;128 133 double *I_out; 129 PyObject *gen_obj;130 134 GenI *sld2i; 131 135 132 if (!PyArg_ParseTuple(args, "OiOO", &gen_obj, &npoints, &q_obj, &I_out_obj)) return NULL; 133 OUTVECTOR(q_obj, q, n_out); 136 if (!PyArg_ParseTuple(args, "OOO", &gen_obj, &q_obj, &I_out_obj)) return NULL; 137 sld2i = (GenI *)PyCapsule_GetPointer(gen_obj, "GenI"); 138 INVECTOR(q_obj, q, n_q); 134 139 OUTVECTOR(I_out_obj, I_out, n_out); 135 140 136 141 // Sanity check 137 //if(n_in!=n_out) return Py_BuildValue("i",-1); 138 139 // Set the array pointers 140 sld2i = (GenI *)PyCapsule_GetPointer(gen_obj, "GenI"); 141 142 genicom(sld2i, npoints, q, I_out); 143 //return PyCObject_FromVoidPtr(s, del_genicom); 142 //if (n_q!=n_out) return Py_BuildValue("i",-1); 143 144 genicom(sld2i, (int)n_q, q, I_out); 144 145 return Py_BuildValue("i",1); 145 146 } -
src/sas/sascalc/calculator/instrument.py
r574adc7 rb8080e1 314 314 """ 315 315 To plot the wavelength spactrum 316 : requir ment: matplotlib.pyplot316 : requirement: matplotlib.pyplot 317 317 """ 318 318 try: -
src/sas/sascalc/calculator/resolution_calculator.py
r8f83719f rb8080e1 1007 1007 try: 1008 1008 detector_offset = self.sample2detector_distance[1] 1009 except Exception as ex:1010 logger.error( ex)1009 except: 1010 logger.error(sys.exc_value) 1011 1011 1012 1012 # detector size in [no of pix_x,no of pix_y] … … 1057 1057 # qx_value and qy_value values in array 1058 1058 qx_value = qx_value.repeat(detector_pix_nums_y) 1059 qx_value = qx_value.reshape( int(detector_pix_nums_x), int(detector_pix_nums_y))1059 qx_value = qx_value.reshape(detector_pix_nums_x, detector_pix_nums_y) 1060 1060 qy_value = qy_value.repeat(detector_pix_nums_x) 1061 qy_value = qy_value.reshape( int(detector_pix_nums_y), int(detector_pix_nums_x))1061 qy_value = qy_value.reshape(detector_pix_nums_y, detector_pix_nums_x) 1062 1062 qy_value = qy_value.transpose() 1063 1063 … … 1094 1094 output.qx_data = qx_value 1095 1095 output.qy_data = qy_value 1096 except Exception as ex:1097 logger.error( ex)1096 except: 1097 logger.error(sys.exc_value) 1098 1098 1099 1099 return output -
src/sas/sascalc/calculator/sas_gen.py
rb58265c3 rb8080e1 118 118 self.is_avg = is_avg 119 119 120 def _gen(self, x, y, i):120 def _gen(self, qx, qy): 121 121 """ 122 122 Evaluate the function … … 129 129 pos_y = self.data_y 130 130 pos_z = self.data_z 131 len_x = len(pos_x)132 131 if self.is_avg is None: 133 len_x *= -1134 132 pos_x, pos_y, pos_z = transform_center(pos_x, pos_y, pos_z) 135 len_q = len(x)136 133 sldn = copy.deepcopy(self.data_sldn) 137 134 sldn -= self.params['solvent_SLD'] 138 model = mod.new_GenI(len_x, pos_x, pos_y, pos_z, 139 sldn, self.data_mx, self.data_my, 140 self.data_mz, self.data_vol, 141 self.params['Up_frac_in'], 142 self.params['Up_frac_out'], 143 self.params['Up_theta']) 144 if y == []: 145 mod.genicom(model, len_q, x, i) 146 else: 147 mod.genicomXY(model, len_q, x, y, i) 135 # **** WARNING **** new_GenI holds pointers to numpy vectors 136 # be sure that they are contiguous double precision arrays and make 137 # sure the GC doesn't eat them before genicom is called. 138 # TODO: rewrite so that the parameters are passed directly to genicom 139 args = ( 140 (1 if self.is_avg else 0), 141 pos_x, pos_y, pos_z, 142 sldn, self.data_mx, self.data_my, 143 self.data_mz, self.data_vol, 144 self.params['Up_frac_in'], 145 self.params['Up_frac_out'], 146 self.params['Up_theta']) 147 model = mod.new_GenI(*args) 148 if len(qy): 149 qx, qy = _vec(qx), _vec(qy) 150 I_out = np.empty_like(qx) 151 #print("npoints", qx.shape, "npixels", pos_x.shape) 152 mod.genicomXY(model, qx, qy, I_out) 153 #print("I_out after", I_out) 154 else: 155 qx = _vec(qx) 156 I_out = np.empty_like(qx) 157 mod.genicom(model, qx, I_out) 148 158 vol_correction = self.data_total_volume / self.params['total_volume'] 149 return self.params['scale'] * vol_correction * i + \ 150 self.params['background'] 159 result = (self.params['scale'] * vol_correction * I_out 160 + self.params['background']) 161 return result 151 162 152 163 def set_sld_data(self, sld_data=None): … … 156 167 self.sld_data = sld_data 157 168 self.data_pos_unit = sld_data.pos_unit 158 self.data_x = sld_data.pos_x159 self.data_y = sld_data.pos_y160 self.data_z = sld_data.pos_z161 self.data_sldn = sld_data.sld_n162 self.data_mx = sld_data.sld_mx163 self.data_my = sld_data.sld_my164 self.data_mz = sld_data.sld_mz165 self.data_vol = sld_data.vol_pix169 self.data_x = _vec(sld_data.pos_x) 170 self.data_y = _vec(sld_data.pos_y) 171 self.data_z = _vec(sld_data.pos_z) 172 self.data_sldn = _vec(sld_data.sld_n) 173 self.data_mx = _vec(sld_data.sld_mx) 174 self.data_my = _vec(sld_data.sld_my) 175 self.data_mz = _vec(sld_data.sld_mz) 176 self.data_vol = _vec(sld_data.vol_pix) 166 177 self.data_total_volume = sum(sld_data.vol_pix) 167 178 self.params['total_volume'] = sum(sld_data.vol_pix) … … 180 191 :return: (I value) 181 192 """ 182 if x.__class__.__name__ == 'list':193 if isinstance(x, list): 183 194 if len(x[1]) > 0: 184 195 msg = "Not a 1D." 185 196 raise ValueError(msg) 186 i_out = np.zeros_like(x[0])187 197 # 1D I is found at y =0 in the 2D pattern 188 out = self._gen(x[0], [] , i_out)198 out = self._gen(x[0], []) 189 199 return out 190 200 else: … … 199 209 :Use this runXY() for the computation 200 210 """ 201 if x.__class__.__name__ == 'list': 202 i_out = np.zeros_like(x[0]) 203 out = self._gen(x[0], x[1], i_out) 204 return out 211 if isinstance(x, list): 212 return self._gen(x[0], x[1]) 205 213 else: 206 214 msg = "Q must be given as list of qx's and qy's" … … 214 222 where qx,qy are 1D ndarrays (for 2D). 215 223 """ 216 if qdist.__class__.__name__ == 'list': 217 if len(qdist[1]) < 1: 218 out = self.run(qdist) 219 else: 220 out = self.runXY(qdist) 221 return out 224 if isinstance(qdist, list): 225 return self.run(qdist) if len(qdist[1]) < 1 else self.runXY(qdist) 222 226 else: 223 227 mesg = "evalDistribution is expecting an ndarray of " 224 228 mesg += "a list [qx,qy] where qx,qy are arrays." 225 229 raise RuntimeError(mesg) 230 231 def _vec(v): 232 return np.ascontiguousarray(v, 'd') 226 233 227 234 class OMF2SLD(object): … … 1041 1048 self.line_z = line_z 1042 1049 1050 def _get_data_path(*path_parts): 1051 from os.path import realpath, join as joinpath, dirname, abspath 1052 # in sas/sascalc/calculator; want sas/sasview/test 1053 return joinpath(dirname(realpath(__file__)), 1054 '..', '..', 'sasview', 'test', *path_parts) 1055 1043 1056 def test_load(): 1044 1057 """ … … 1046 1059 """ 1047 1060 from mpl_toolkits.mplot3d import Axes3D 1048 current_dir = os.path.abspath(os.path.curdir) 1049 print(current_dir) 1050 for i in range(6): 1051 current_dir, _ = os.path.split(current_dir) 1052 tfile = os.path.join(current_dir, "test", "CoreXY_ShellZ.txt") 1053 ofile = os.path.join(current_dir, "test", "A_Raw_Example-1.omf") 1054 if os.path.isfile(tfile): 1055 tfpath = tfile 1056 ofpath = ofile 1057 break 1061 tfpath = _get_data_path("1d_data", "CoreXY_ShellZ.txt") 1062 ofpath = _get_data_path("coordinate_data", "A_Raw_Example-1.omf") 1063 if not os.path.isfile(tfpath) or not os.path.isfile(ofpath): 1064 raise ValueError("file(s) not found: %r, %r"%(tfpath, ofpath)) 1058 1065 reader = SLDReader() 1059 1066 oreader = OMFReader() 1060 output = decode(reader.read(tfpath))1061 ooutput = decode(oreader.read(ofpath))1067 output = reader.read(tfpath) 1068 ooutput = oreader.read(ofpath) 1062 1069 foutput = OMF2SLD() 1063 1070 foutput.set_data(ooutput) … … 1088 1095 plt.show() 1089 1096 1097 def test_save(): 1098 ofpath = _get_data_path("coordinate_data", "A_Raw_Example-1.omf") 1099 if not os.path.isfile(ofpath): 1100 raise ValueError("file(s) not found: %r"%(ofpath,)) 1101 oreader = OMFReader() 1102 omfdata = oreader.read(ofpath) 1103 omf2sld = OMF2SLD() 1104 omf2sld.set_data(omfdata) 1105 writer = SLDReader() 1106 writer.write("out.txt", omf2sld.output) 1107 1090 1108 def test(): 1091 1109 """ 1092 1110 Test code 1093 1111 """ 1094 current_dir = os.path.abspath(os.path.curdir) 1095 for i in range(3): 1096 current_dir, _ = os.path.split(current_dir) 1097 ofile = os.path.join(current_dir, "test", "A_Raw_Example-1.omf") 1098 if os.path.isfile(ofile): 1099 ofpath = ofile 1100 break 1112 ofpath = _get_data_path("coordinate_data", "A_Raw_Example-1.omf") 1113 if not os.path.isfile(ofpath): 1114 raise ValueError("file(s) not found: %r"%(ofpath,)) 1101 1115 oreader = OMFReader() 1102 ooutput = decode(oreader.read(ofpath)) 1103 foutput = OMF2SLD() 1104 foutput.set_data(ooutput) 1105 writer = SLDReader() 1106 writer.write(os.path.join(os.path.dirname(ofpath), "out.txt"), 1107 foutput.output) 1116 omfdata = oreader.read(ofpath) 1117 omf2sld = OMF2SLD() 1118 omf2sld.set_data(omfdata) 1108 1119 model = GenSAS() 1109 model.set_sld_data(foutput.output) 1110 x = np.arange(1000)/10000. + 1e-5 1111 y = np.arange(1000)/10000. + 1e-5 1112 i = np.zeros(1000) 1113 model.runXY([x, y, i]) 1120 model.set_sld_data(omf2sld.output) 1121 x = np.linspace(0, 0.1, 11)[1:] 1122 return model.runXY([x, x]) 1114 1123 1115 1124 if __name__ == "__main__": 1125 #test_load() 1126 #test_save() 1127 #print(test()) 1116 1128 test() 1117 test_load()
Note: See TracChangeset
for help on using the changeset viewer.