diff --git a/gecatsim/clib_build/src/analytic_projector.c b/gecatsim/clib_build/src/analytic_projector.c index bb9b5ad..4ccd5d4 100644 --- a/gecatsim/clib_build/src/analytic_projector.c +++ b/gecatsim/clib_build/src/analytic_projector.c @@ -32,7 +32,7 @@ int n_col_oversample_add_xtalk = 1; int n_row_oversample_add_xtalk = 1; -struct module_info +struct analytic_module_info { double *Height; double *Width; @@ -48,7 +48,7 @@ struct module_info int moduleOverlapType; }; -struct module_info modules = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0,0,0,0}; +struct analytic_module_info analytic_modules = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0,0,0,0}; struct pnt { @@ -67,7 +67,7 @@ struct bounding_info struct bounding_info bounding = {NULL,NULL}; -struct phantom_info +struct analytic_phantom_info { int numObjects; int *objectType; @@ -83,16 +83,16 @@ struct phantom_info double xbounds[2]; }; -struct phantom_info phantom = {0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,{0,0}}; +struct analytic_phantom_info analytic_phantom = {0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,{0,0}}; -struct material_info +struct analytic_material_info { int materialCount; int eBinCount; double *muTable; }; -struct material_info materials = {0,0,NULL}; +struct analytic_material_info analytic_materials = {0,0,NULL}; struct projector_args { @@ -463,12 +463,12 @@ int quartic_intersect_C(double *a0,double *Qlp,double *Qrp,double shp,double *al int i,out; double scale,displ,a0t[3],tmp[3],aa,b,c,d,e,f,C[5],*Ql,*Qr,sh; - Ql = &phantom.Qmatrix[obj*18]; - // printf("%1.12lf %1.12lf",phantom.Qmatrix[obj); + Ql = &analytic_phantom.Qmatrix[obj*18]; + // printf("%1.12lf %1.12lf",analytic_phantom.Qmatrix[obj); // for(i = 1;i<9;i++) printf("L %1.12lf %1.12lf diff: %1.12lf\r\n",Ql[i],Qlp[i],Ql[i]-Qlp[i]); - Qr = &phantom.Qmatrix[obj*18+9]; + Qr = &analytic_phantom.Qmatrix[obj*18+9]; // for(i = 1;i<9;i++) printf("R %1.12lf %1.12lf diff: %1.12lf\r\n",Qr[i],Qrp[i],Qr[i]-Qrp[i]); - sh = phantom.shape[obj]; + sh = analytic_phantom.shape[obj]; // printf("s diff: %1.12lf\r\n",sh-shp); scale = 1.0; @@ -505,9 +505,9 @@ int quartic_intersect(double *a0,double *alpha,double *tc,int obj) int i,out; double scale,displ,a0t[3],tmp[3],aa,b,c,d,e,f,C[5],*Ql,*Qr,sh; - Ql = &phantom.Qmatrix[obj*18]; - Qr = &phantom.Qmatrix[obj*18+9]; - sh = phantom.shape[obj]; + Ql = &analytic_phantom.Qmatrix[obj*18]; + Qr = &analytic_phantom.Qmatrix[obj*18+9]; + sh = analytic_phantom.shape[obj]; scale = 1.0; displ = 0; for(i = 0;i<3;i++) displ += a0[i]*a0[i]; @@ -541,12 +541,12 @@ DLLEXPORT int clip_all(double *a,double *alpha,double rayLength,double *tc2,int double b[3], s1, s2, tcrit, tmin, tmax, *eta, *s, den; int j, cp, firstPlane, materialIndex; - firstPlane = phantom.clipStartIndex[i]; - eta = &phantom.clipNormalVector[firstPlane*3]; - s = &phantom.clipDistance[firstPlane]; - cp = phantom.nClipPlanes[i]; - den = phantom.density[i]; - materialIndex = phantom.materialInd[i]; + firstPlane = analytic_phantom.clipStartIndex[i]; + eta = &analytic_phantom.clipNormalVector[firstPlane*3]; + s = &analytic_phantom.clipDistance[firstPlane]; + cp = analytic_phantom.nClipPlanes[i]; + den = analytic_phantom.density[i]; + materialIndex = analytic_phantom.materialInd[i]; tmin = -VERY_BIG;tmax = VERY_BIG; for(j = 0;j<3;j++) b[j] = a[j]+rayLength*alpha[j]; @@ -604,8 +604,8 @@ DLLEXPORT int quadratic_intersect(double *a0,double *alpha,int pars11,double *tc int out; double A,B,C,tmp,*Q,k; // The quadratic formula is of the form A t^2 + B t + C = 0 - Q = &phantom.Qmatrix[obj*18]; - k = phantom.shape[obj]; + Q = &analytic_phantom.Qmatrix[obj*18]; + k = analytic_phantom.shape[obj]; C = quadratic_form(a0,Q,a0)-k; B = 2.0*quadratic_form(alpha,Q,a0); A = quadratic_form(alpha,Q,alpha); @@ -665,29 +665,29 @@ double* my_memcpyd(double *src, double *dest, int bytes) DLLEXPORT void set_src_info(double *sourceWeights, int nSubSources) { - modules.sourceWeights = my_memcpyd(sourceWeights, modules.sourceWeights, nSubSources*sizeof(double)); - modules.nSubSources = nSubSources; + analytic_modules.sourceWeights = my_memcpyd(sourceWeights, analytic_modules.sourceWeights, nSubSources*sizeof(double)); + analytic_modules.nSubSources = nSubSources; } DLLEXPORT void set_module_info(double *Height, double *Width, int *Pix, double *Coords, int *Sub, double *Sampling, double *Weight, int nModuleTypes, int maxPix, int maxSubDets, int moduleOverlapType) { int i; - modules.Height = my_memcpyd(Height, modules.Height, nModuleTypes*sizeof(double)); - modules.Width = my_memcpyd(Width, modules.Width, nModuleTypes*sizeof(double)); + analytic_modules.Height = my_memcpyd(Height, analytic_modules.Height, nModuleTypes*sizeof(double)); + analytic_modules.Width = my_memcpyd(Width, analytic_modules.Width, nModuleTypes*sizeof(double)); for(i = 0; i < nModuleTypes; i++) { - modules.Height[i] = MAX(1e-7,modules.Height[i]); - modules.Width[i] = MAX(1e-7,modules.Width[i]); + analytic_modules.Height[i] = MAX(1e-7,analytic_modules.Height[i]); + analytic_modules.Width[i] = MAX(1e-7,analytic_modules.Width[i]); } - modules.Pix = my_memcpyi(Pix, modules.Pix, nModuleTypes*sizeof(int)); - modules.Coords = my_memcpyd(Coords, modules.Coords, maxPix*2*nModuleTypes*sizeof(double)); - modules.Sub = my_memcpyi(Sub, modules.Sub, nModuleTypes*sizeof(int)); - modules.Sampling = my_memcpyd(Sampling, modules.Sampling, 2*maxSubDets*nModuleTypes*sizeof(double)); - modules.Weight = my_memcpyd(Weight, modules.Weight, maxSubDets*nModuleTypes*sizeof(double)); - modules.maxPixPerModule = maxPix; - modules.maxSubDets = maxSubDets; - modules.moduleOverlapType = moduleOverlapType; + analytic_modules.Pix = my_memcpyi(Pix, analytic_modules.Pix, nModuleTypes*sizeof(int)); + analytic_modules.Coords = my_memcpyd(Coords, analytic_modules.Coords, maxPix*2*nModuleTypes*sizeof(double)); + analytic_modules.Sub = my_memcpyi(Sub, analytic_modules.Sub, nModuleTypes*sizeof(int)); + analytic_modules.Sampling = my_memcpyd(Sampling, analytic_modules.Sampling, 2*maxSubDets*nModuleTypes*sizeof(double)); + analytic_modules.Weight = my_memcpyd(Weight, analytic_modules.Weight, maxSubDets*nModuleTypes*sizeof(double)); + analytic_modules.maxPixPerModule = maxPix; + analytic_modules.maxSubDets = maxSubDets; + analytic_modules.moduleOverlapType = moduleOverlapType; } DLLEXPORT void set_bounding_info(int numObjs, int *vertexStartInd, double *vertLocs, int numVerts) @@ -697,38 +697,38 @@ DLLEXPORT void set_bounding_info(int numObjs, int *vertexStartInd, double *vertL bounding.vertexStartIndex = my_memcpyi(vertexStartInd, bounding.vertexStartIndex, (numObjs+1)*sizeof(int)); bounding.vertexLocations = my_memcpyd(vertLocs, bounding.vertexLocations, sizeof(double)*numVerts*3); - phantom.xbounds[0]=VERY_BIG; - phantom.xbounds[1]=-VERY_BIG; + analytic_phantom.xbounds[0]=VERY_BIG; + analytic_phantom.xbounds[1]=-VERY_BIG; for(i=0;iphantom.xbounds[1]) phantom.xbounds[1]=bounding.vertexLocations[i*3]; - if (bounding.vertexLocations[i*3]analytic_phantom.xbounds[1]) analytic_phantom.xbounds[1]=bounding.vertexLocations[i*3]; + if (bounding.vertexLocations[i*3]maxVertexPoints) maxVertexPoints = vertexPoints[i]; @@ -1695,7 +1695,7 @@ void compute_object_projections(double *srcHullPoints, int nSrcHullPoints, int m indexList = malloc(sizeof(int)*(maxVertexPoints*nSrcHullPoints+moduleEdges*2)); //dbug(3,"COP1\r\n"); - for(i = 0;i0) dbug(2,"listLength: %d\r\n",listLength); @@ -1788,7 +1788,7 @@ int any_objects_1(int moduleNumber, void *boundaries) int any_objs = 0, i; struct height_lims *heightLims = (struct height_lims *) boundaries; - for(i = 0;i < phantom.numObjects;i++) + for(i = 0;i < analytic_phantom.numObjects;i++) { //dbug(3,"Module number: %d minHeight[%d] = %lf maxHeight[%d] = %lf Different? %d",moduleNumber,i,heightLims.min[i],i,heightLims.max[i],(int)(heightLims.min[i] != heightLims.max[i])); if (heightLims[i].min != heightLims[i].max) @@ -1803,7 +1803,7 @@ int any_objects_2(int moduleNumber, void *boundaries) int any_objs = 0, i; struct box_lims *boxLims = (struct box_lims *) boundaries; - for(i = 0;i < phantom.numObjects;i++) + for(i = 0;i < analytic_phantom.numObjects;i++) { if (boxLims[i].minR != boxLims[i].maxR) {any_objs++;break;} @@ -1817,7 +1817,7 @@ void build_object_list1(double *pix_vlims, int *objectList, int *n_objlist, int int i; struct height_lims *heightLims = (struct height_lims *) boundaries; - for(i = 0;i= v+pix_vlims[0])) {objectList[n_objlist[0]] = i;n_objlist[0]++;} } @@ -1828,7 +1828,7 @@ void build_object_list2(double *pix_ulims, double *pix_vlims, int *objectList, i int i; struct box_lims *boxLims = (struct box_lims *) boundaries; - for(i = 0;i= uv[1]+pix_vlims[0]),(int) (boxLims[i].minR <= uv[0]+pix_ulims[1]),(int) (boxLims[i].maxR >= uv[0]+pix_ulims[0])); dbug(3,"parts for third one: %1.5lf %1.5lf %1.5lf %1.5lf \r\n ",boxLims[0].minR,uv[0],pix_ulims[1],uv[0]+pix_ulims[1]); @@ -1853,13 +1853,13 @@ void intersections_loop(double *detCenter, double *right, double *up, double *sa double *materialBuffer = NULL; double *pValueSpectrum = NULL; - materialBuffer = malloc(sizeof(double)*materials.materialCount); - pValueSpectrum = malloc(sizeof(double)*materials.eBinCount); + materialBuffer = malloc(sizeof(double)*analytic_materials.materialCount); + pValueSpectrum = malloc(sizeof(double)*analytic_materials.eBinCount); for(l = 0;l0.0) dbug(1,"materialBuffer[0]: %4.4lf\r\n",materialBuffer[0]); - for(m = 0;m0.0) dbug(1,"pValueSpectrum[0]: %4.4lf\r\n",pValueSpectrum[0]); //------------------- Accurate Detector Model, Mingye if(Accurate_Detector_Model_is_ON) { - for(m = 0;m < materials.eBinCount;m++) + for(m = 0;m < analytic_materials.eBinCount;m++) { - int the_index = (detIndex*n_col_oversample + (int)(l/n_row_oversample_add_xtalk))*materials.eBinCount + m; - thisView[the_index] += subviewWeight*detWeights[l]*modules.sourceWeights[k]*exp(-pValueSpectrum[m]); + int the_index = (detIndex*n_col_oversample + (int)(l/n_row_oversample_add_xtalk))*analytic_materials.eBinCount + m; + thisView[the_index] += subviewWeight*detWeights[l]*analytic_modules.sourceWeights[k]*exp(-pValueSpectrum[m]); } } //------------------- else { - for(m = 0;m < materials.eBinCount;m++) - thisView[detIndex*materials.eBinCount+m] += subviewWeight*detWeights[l]*modules.sourceWeights[k]*exp(-pValueSpectrum[m]); + for(m = 0;m < analytic_materials.eBinCount;m++) + thisView[detIndex*analytic_materials.eBinCount+m] += subviewWeight*detWeights[l]*analytic_modules.sourceWeights[k]*exp(-pValueSpectrum[m]); } } } @@ -1915,24 +1915,24 @@ void set_Accurate_Detector_Model(double *Paras) } if(Paras[5]==1 && Paras[1]<3 && Paras[2]==1) //col_xtalk>0, first subview in the first two views - if(modules.Sub[0]>n_col_oversample*n_row_oversample_add_xtalk) + if(analytic_modules.Sub[0]>n_col_oversample*n_row_oversample_add_xtalk) { int n1; - modules.maxSubDets-=2*n_row_oversample_add_xtalk; - modules.Sub[0]-=2*n_row_oversample_add_xtalk; + analytic_modules.maxSubDets-=2*n_row_oversample_add_xtalk; + analytic_modules.Sub[0]-=2*n_row_oversample_add_xtalk; double sum_weights=0; - for(n1=0;n1pix_vlims[1]) pix_vlims[1] = sampling[2*i+1]; if(sampling[2*i] phantom.numObjects) - //dbug(2,"nListObjects = %d, phantom.numObjects = %d\r\n", nListObjects, phantom.numObjects); + //if (nListObjects > analytic_phantom.numObjects) + //dbug(2,"nListObjects = %d, analytic_phantom.numObjects = %d\r\n", nListObjects, analytic_phantom.numObjects); //if (nListObjects > 0) //dbug(2,"nListObjects = %d, k = %d\r\n", nListObjects, k); break; @@ -2078,21 +2078,21 @@ DLLEXPORT void Projector(double *Paras, double subviewWeight, double *thisView, //------------------- Accurate Detector Model, Mingye if(Accurate_Detector_Model_is_ON) { - for(m = 0;m0) { min_val = VERY_BIG; - for(k = 0;k 1: @@ -33,6 +173,7 @@ def PhantomWrapper(cfg): origCfgPhantom = CopyCfgPhantom(cfg) cfg_nom_list = [] for thiscfg in cfg_list: + # use cfg here because we want to use cfg's other attr than phantom without the heavy copy overhead cfg = CopyCfgPhantom(thiscfg, cfg) cfg = feval(cfg.phantom.callback, cfg) if hasattr(cfg.phantom, 'numberOfMaterials'): @@ -43,14 +184,27 @@ def PhantomWrapper(cfg): cfg_nom_list.append(None) # restore original CFG - cfg = CopyCfgPhantom(origCfgPhantom, cfg) - cfg.phantom.numberOfMaterials = cfg_nom_list + if is_dynamic_phantom: + cfg = CopyCfgPhantom(_origCfgPhantom, cfg) + cfg.phantom.numberOfMaterials = [None]*len(cfg.phantom.callback) + cfg.phantom.numberOfMaterials[cfg.viewId] = cfg_nom_list + else: + cfg = CopyCfgPhantom(origCfgPhantom, cfg) + cfg.phantom.numberOfMaterials = cfg_nom_list else: cfg = feval(cfg.phantom.callback, cfg) + return cfg def ProjectorWrapper(cfg, viewId, subViewId): + is_dynamic_phantom = False + if isinstance(cfg.phantom.callback, list) and isinstance(cfg.phantom.callback[0], list): + is_dynamic_phantom = True + _cfg_list = SplitCfgPhantom(cfg) + _origCfgPhantom = CopyCfgPhantom(cfg) + cfg = CopyCfgPhantom(_cfg_list[cfg.viewId], cfg) + if isinstance(cfg.phantom.projectorCallback, list): cfg_list = SplitCfgPhantom(cfg) origCfgPhantom = CopyCfgPhantom(cfg) @@ -62,4 +216,7 @@ def ProjectorWrapper(cfg, viewId, subViewId): else: cfg = feval(cfg.phantom.projectorCallback, cfg, viewId, subViewId) + if is_dynamic_phantom: + cfg = CopyCfgPhantom(_origCfgPhantom, cfg) + return cfg diff --git a/gecatsim/pyfiles/Phantom_Analytic.py b/gecatsim/pyfiles/Phantom_Analytic.py index fc7aeb4..f5350b2 100644 --- a/gecatsim/pyfiles/Phantom_Analytic.py +++ b/gecatsim/pyfiles/Phantom_Analytic.py @@ -648,10 +648,12 @@ def Phantom_Analytic_SetObjects(objs=None, debug=False): phantObject[i]['s'] = objs['clip'][i][:,3].T else: #TODO: consider change to [] - #phantObject[i]['eta'] = [] - #phantObject[i]['s'] = [] - phantObject[i]['eta'] = None - phantObject[i]['s'] = None + # phantObject[i]['eta'] = [] + # phantObject[i]['s'] = [] + # phantObject[i]['eta'] = None + # phantObject[i]['s'] = None + phantObject[i]['eta'] = np.empty((3, 0)) + phantObject[i]['s'] = np.empty(0) p1=pars[6]*np.pi / 180 p2=pars[7]*np.pi / 180 p3=pars[8]*np.pi / 180 diff --git a/gecatsim/pyfiles/Xray_Filter.py b/gecatsim/pyfiles/Xray_Filter.py index dadf7b5..a50dbd3 100644 --- a/gecatsim/pyfiles/Xray_Filter.py +++ b/gecatsim/pyfiles/Xray_Filter.py @@ -20,49 +20,59 @@ def Xray_Filter(cfg): def flat_filter(cfg): ''' - Apply the transmittance of flat filter at source side, dim: [Ebin, totalNumCells] + Apply the transmittance of flat filter at source side, optimized version ''' - cosineFactors = 1/np.cos(cfg.det.gammas)/np.cos(cfg.det.alphas) + cosineFactors = 1 / (np.cos(cfg.det.gammas) * np.cos(cfg.det.alphas)) Evec = cfg.sim.Evec - trans = np.ones([cfg.det.totalNumCells, cfg.spec.nEbin], dtype = np.single) + trans = np.ones([cfg.det.totalNumCells, cfg.spec.nEbin], dtype=np.single) if hasattr(cfg.protocol, "flatFilter"): - for ii in range(0, round(len(cfg.protocol.flatFilter)/2)): + # Pre-allocate array for mu values + mu_total = np.zeros_like(Evec, dtype=np.single) + + for ii in range(0, len(cfg.protocol.flatFilter) // 2): material = cfg.protocol.flatFilter[2*ii] depth = cfg.protocol.flatFilter[2*ii+1] mu = GetMu(material, Evec) - trans *= np.exp(-depth*0.1*cosineFactors @ mu.reshape(1, mu.size)) - cfg.src.filterTrans *= trans + mu_total += depth * 0.1 * mu + + # Single exponential calculation instead of multiple + trans *= np.exp(-np.outer(cosineFactors, mu_total)) + cfg.src.filterTrans *= trans return cfg def bowtie_filter(cfg): ''' - Apply the transmittance of bowtie filter, dim: [Ebin, totalNumCells] + Apply the transmittance of bowtie filter, optimized version ''' if not cfg.protocol.bowtie: return cfg - # find bowtie file bowtieFile = my_path.find("bowtie", cfg.protocol.bowtie, ".txt") - - # read bowtie file data = np.loadtxt(bowtieFile, dtype=np.single, comments=['#', '%']) gammas0 = data[:, 0] - t0 = data[:, 1:] # thickness in cm + t0 = data[:, 1:] bowtieMaterials = ['Al', 'graphite', 'Cu', 'Ti'] Evec = cfg.spec.Evec gammas1 = cfg.det.gammas + cos_alphas_inv = 1.0 / np.cos(cfg.det.alphas) + + # Pre-compute all mu values + mu_array = np.array([GetMu(material, Evec) for material in bowtieMaterials], dtype=np.single) + + # Vectorized interpolation for all materials at once + f_interps = [interpolate.interp1d(gammas0, t0[:, i], kind='linear', fill_value='extrapolate', assume_sorted=True) + for i in range(len(bowtieMaterials))] - muT = 0 - for i in range(len(bowtieMaterials)): - mu = GetMu(bowtieMaterials[i], Evec) - f = interpolate.interp1d(gammas0, t0[:, i], kind='linear', fill_value='extrapolate') - t1 = f(gammas1)/np.cos(cfg.det.alphas) - muT += t1 @ mu.reshape(1, mu.size) + # Compute total attenuation in one operation + muT = np.zeros((len(gammas1), len(Evec)), dtype=np.single) + for i, f in enumerate(f_interps): + t1 = f(gammas1) * cos_alphas_inv + muT += np.outer(t1, mu_array[i]) trans = np.exp(-muT) cfg.src.filterTrans *= trans