From 54febb8bbf8d89978ef79138b433c91b50328bd2 Mon Sep 17 00:00:00 2001 From: Andrei Semena Date: Mon, 16 Jun 2025 16:21:19 +0300 Subject: [PATCH] energy interpolation added, convergence condition updated --- chan_psf.c | 279 +++++++++++++++++++++++++++++++++++++++++++ rate_solvers.c | 2 +- source_detection2.py | 122 +++++++++++++++++++ 3 files changed, 402 insertions(+), 1 deletion(-) create mode 100644 source_detection2.py diff --git a/chan_psf.c b/chan_psf.c index 1c646ae..27b7590 100644 --- a/chan_psf.c +++ b/chan_psf.c @@ -15,6 +15,120 @@ double * psfvalfromptr(double * psfdata, npy_intp * dims, int k, int xi, int yi) return psfdata + (k*dims[1] + xi)*dims[2] + yi; }; +double * eepsfvalfromptr(double * psfdata, npy_intp * dims, int k, int ei, int xi, int yi) +{ + //printf("check didx %d %.2e\n", ((dims[1]*eidx + k)*dims[2] + xi)*dims[3] + yi, *( psfdata + ((dims[1]*eidx + k)*dims[2] + xi)*dims[3] + yi)); + return psfdata + ((k*dims[1] + ei)*dims[2] + xi)*dims[3] + yi; +}; + +static PyObject * put_psf_on(PyObject *self, PyObject *args) +{ + + PyArrayObject *psfi, *x, *y, *roll, *xc, *yc, *smat, *emap; + if (!PyArg_ParseTuple(args, "OOOOOOOO", &psfi, &x, &y, &roll, &xc, &yc, &smat, &emap)) return NULL; + + int loc, ctr, msum=0; + double x1, y1, dx, dy; + npy_intp snew = {emap->dimensions[0]}; + PyArrayObject * cmap = PyArray_SimpleNew(1, &snew, NPY_DOUBLE); + double * cmapd = (double*) cmap->data; + double * smatd = (double*) smat->data; + + double *ca = (double*)malloc(sizeof(double)*x->dimensions[0]); + double *sa = (double*)malloc(sizeof(double)*x->dimensions[0]); + + double* nparrptr = (double*) roll->data; + + for (ctr=0; ctr < x->dimensions[0]; ctr++) + { + ca[ctr] = cos(nparrptr[ctr]); + sa[ctr] = sin(nparrptr[ctr]); + }; + + Py_BEGIN_ALLOW_THREADS; + + double inpixdx, inpixdy; + long * k = (long*)psfi->data; + double* xptr = (double*) x->data; + double* yptr = (double*) y->data; + double *xcptr = (double*) xc->data; + double *ycptr = (double*) yc->data; + + double pval, eloc, p2, p3; + int idx1d, idx2d; + printf("check smap %d %d %d\n", smat->dimensions[0], smat->dimensions[1], smat->dimensions[2]); + printf("%f %f %f %f %f\n", nparrptr[0], xptr[0], yptr[0], xcptr[0], ycptr[0]); + + for (loc=0; loc < xc->dimensions[0]; loc++) // loop over sky locations + { + //printf("loc %d %d %d \n", loc); + msum = 0; + for (ctr=0; ctr < psfi->dimensions[0]; ctr++) // for each sky location loop over all provided events + { + x1 = (xcptr[loc] - xptr[ctr]); + y1 = (ycptr[loc] - yptr[ctr]); + //rotate by the event roll angle, dx dy centered at the psf center (central pixel of 101x101 map) + dx = x1*ca[ctr] - y1*sa[ctr]; //+ 50; + dy = y1*ca[ctr] + x1*sa[ctr]; // + 50.; + // temporary hardcode psf shape is 101x101 + + //current psf shape is 101: + if ((dx > -50) && (dx < 50)) + { + if ((dy > -50) && (dy < 50)) + { + idx1d = (int)((dx + 50.5)); // float dx from -0.5 to 0.5 should fell in the 50-th pixel + idx2d = (int)((dy + 50.5)); + pval = * psfvalfromptr(smatd, smat->dimensions, *(k + ctr), idx1d, idx2d); + + //naive interpolation block + //------------------------------------------------------------------------------------------------------- + inpixdx = dx - (idx1d - 50); + inpixdy = dy - (idx2d - 50); + if (inpixdx > 0.) + { + p2 = * psfvalfromptr(smatd, smat->dimensions, *(k + ctr), idx1d + 1, idx2d); + if (inpixdy > 0.) + { + p3 = * psfvalfromptr(smatd, smat->dimensions, * (k + ctr), idx1d, idx2d + 1); + }else{ + inpixdy = -inpixdy; + p3 = * psfvalfromptr(smatd, smat->dimensions, * (k + ctr), idx1d, idx2d - 1); + } + }else{ + p2 = * psfvalfromptr(smatd, smat->dimensions, * (k + ctr), idx1d - 1, idx2d); + inpixdx = -inpixdx; + if (inpixdy > 0.) + { + p3 = * psfvalfromptr(smatd, smat->dimensions, * (k + ctr), idx1d, idx2d + 1); + }else{ + inpixdy = -inpixdy; + p3 = * psfvalfromptr(smatd, smat->dimensions, * (k + ctr), idx1d, idx2d - 1); + } + } + printf("pval %f %f %f %f %f idx12&k %d %d %d x1:%f y1:%f dxdy: %f %f\n", pval, p2, p3, inpixdx, inpixdy, idx1d, idx2d, k[ctr], x1, y1, dx, dy); + pval = (pval + inpixdx*(p2 - pval) + inpixdy*(p3 - pval)); + msum = 1; + + }; + }; + + }; + if (msum > 0) + { + *(cmapd + loc) = pval; + + }; + }; + + Py_END_ALLOW_THREADS; + + + PyObject *res = Py_BuildValue("O", cmap); + Py_DECREF(cmap); + return res; +} + static PyObject * solve_for_locations(PyObject *self, PyObject *args) { @@ -184,10 +298,175 @@ static PyObject * solve_for_locations(PyObject *self, PyObject *args) return res; } +static PyObject * solve_for_locations_eintp(PyObject *self, PyObject *args) +{ + //xc, yc --- wcs locations, events has coordinates in the same locations, and psf have the same grid as well + // the only additional parameter to events are pk scale (rate scale in respect to psf) and rotation angle + PyArrayObject *psfi, *eidx, *x, *y, *roll, *pk, *xc, *yc, *smat, *emap; + int loc, ctr; + double x1, y1, dx, dy; + + if (!PyArg_ParseTuple(args, "OOOOOOOOOO", &psfi, &eidx, &x, &y, &roll, &pk, &xc, &yc, &emap, &smat)) return NULL; + // -------------------------- =============== + // those are events properties those for sky smat it array for psf matrices + + npy_intp snew = {xc->dimensions[0]}; + PyArrayObject * cmap = PyArray_SimpleNew(1, &snew, NPY_DOUBLE); + PyArrayObject * pmap = PyArray_SimpleNew(1, &snew, NPY_DOUBLE); + double * cmapd = (double*) cmap->data; + double * pmapd = (double*) pmap->data; + double * smatd = (double*) smat->data; + + + double *ca = (double*)malloc(sizeof(double)*x->dimensions[0]); + double *sa = (double*)malloc(sizeof(double)*x->dimensions[0]); + + double* nparrptr = (double*) roll->data; + + for (ctr=0; ctr < x->dimensions[0]; ctr++) + { + ca[ctr] = cos(nparrptr[ctr]); + sa[ctr] = sin(nparrptr[ctr]); + }; + + double * bw = (double*)malloc(sizeof(double)*psfi->dimensions[0]); //not more then thet will be used for each location + + Py_BEGIN_ALLOW_THREADS; + + double inpixdx, inpixdy; + double * pkd = (double*) pk->data; + long * k = (long*)psfi->data; + double * ek = (double*)eidx->data; + int ei; + double* xptr = (double*) x->data; + double* yptr = (double*) y->data; + double *xcptr = (double*) xc->data; + double *ycptr = (double*) yc->data; + + + long ctr, msum=0; + double lkl, erf; + + double pval, eloc, p2, p3, ptmp; + int idx1d, idx2d; + + //return psfdata + ((k*dims[1] + ei)*dims[2] + xi)*dims[3] + yi; + //printf("psf dim %d %d %d %d\n", smat->dimensions[0], smat->dimensions[1], smat->dimensions[2], smat->dimensions[3]); + //printf("psf %f %f\n", smatd[((0*smat->dimensions[1] + 1*smat->dimensions[2]) + 30)*smat->dimensions[3] + 30], smatd[((0*smat->dimensions[1] + 1*smat->dimensions[2]) + 30)*smat->dimensions[3] + 30]); + + for (loc=0; loc < xc->dimensions[0]; loc++) // loop over sky locations + { + msum = 0; + //printf("loc %d\n", loc); + for (ctr=0; ctr < psfi->dimensions[0]; ctr++) // for each sky location loop over all provided events + { + x1 = (xcptr[loc] - xptr[ctr]); + y1 = (ycptr[loc] - yptr[ctr]); + //rotate by the event roll angle, dx dy centered at the psf center (central pixel of 101x101 map) + dx = x1*ca[ctr] - y1*sa[ctr]; //+ 50; + dy = y1*ca[ctr] + x1*sa[ctr]; // + 50.; + // temporary hardcode psf shape is 101x101 + ei = (int)(ek[ctr]); + erf = ek[ctr] - (double)(ei); + //printf("evt %d ei %d erf %f ek %f\n", ctr, ei, erf, ek[ctr]); + + //current psf shape is 101: + if ((dx > -50) && (dx < 50)) + { + if ((dy > -50) && (dy < 50)) + { + idx1d = (int)((dx + 50.5)); // float dx from -0.5 to 0.5 should fell in the 50-th pixel + idx2d = (int)((dy + 50.5)); + + pval = (* eepsfvalfromptr(smatd, smat->dimensions, *(k + ctr), ei, idx1d, idx2d))*(1. - erf) + (* eepsfvalfromptr(smatd, smat->dimensions, *(k + ctr), ei + 1, idx1d, idx2d))*erf; + + //naive interpolation block + //------------------------------------------------------------------------------------------------------- + inpixdx = dx - (idx1d - 50); + inpixdy = dy - (idx2d - 50); + if (inpixdx > 0.) + { + p2 = (* eepsfvalfromptr(smatd, smat->dimensions, *(k + ctr), ei, idx1d + 1, idx2d))*(1. - erf) + (* eepsfvalfromptr(smatd, smat->dimensions, *(k + ctr), ei + 1, idx1d + 1, idx2d))*erf; + if (inpixdy > 0.) + { + p3 = (* eepsfvalfromptr(smatd, smat->dimensions, *(k + ctr), ei, idx1d, idx2d + 1))*(1. - erf) + (* eepsfvalfromptr(smatd, smat->dimensions, *(k + ctr), ei + 1, idx1d, idx2d + 1))*erf; + }else{ + inpixdy = -inpixdy; + p3 = (* eepsfvalfromptr(smatd, smat->dimensions, *(k + ctr), ei, idx1d, idx2d - 1))*(1. - erf) + (* eepsfvalfromptr(smatd, smat->dimensions, *(k + ctr), ei + 1, idx1d, idx2d - 1))*erf; + } + }else{ + p2 = (* eepsfvalfromptr(smatd, smat->dimensions, *(k + ctr), ei, idx1d - 1, idx2d))*(1. - erf) + (* eepsfvalfromptr(smatd, smat->dimensions, *(k + ctr), ei + 1, idx1d - 1, idx2d))*erf; + inpixdx = -inpixdx; + if (inpixdy > 0.) + { + p3 = (* eepsfvalfromptr(smatd, smat->dimensions, *(k + ctr), ei, idx1d, idx2d + 1))*(1. - erf) + (* eepsfvalfromptr(smatd, smat->dimensions, *(k + ctr), ei + 1, idx1d, idx2d + 1))*erf; + }else{ + inpixdy = -inpixdy; + p3 = (* eepsfvalfromptr(smatd, smat->dimensions, *(k + ctr), ei, idx1d, idx2d - 1))*(1. - erf) + (* eepsfvalfromptr(smatd, smat->dimensions, *(k + ctr), ei + 1, idx1d, idx2d - 1))*erf; + } + } + //printf("pval %f %f %f %f %f %d %d %d\n", pval, p2, p3, inpixdx, inpixdy, idx1d, idx2d, k[ctr]); + pval = (pval + inpixdx*(p2 - pval) + inpixdy*(p3 - pval))* (*(pkd + ctr)); + // interpolation up to here + //------------------------------------------------------------------------------------------------------- + + if (pval > 1e-10) + { + bw[msum] = pval; + //printf("%d %d %d %f %f %f %f %f\n", k[ctr], idx1d, idx2d, inpixdx, inpixdy, dx, dy, pval); + //printf("%d %d %d %f %f %f %f %f\n", k[ctr], idx1d, idx2d, xptr[ctr], yptr[ctr], xcptr[loc], ycptr[loc], pval); + msum += 1; + }; + + }; + }; + + }; + if (msum > 0) + { + eloc = (double) *((double*) emap->data + loc); + pval = get_phc_solution_pkr((double) msum, eloc, bw, msum); + *(cmapd + loc) = pval*eloc; + lkl = 0.; + for (ctr=0; ctr < msum; ctr ++) + { + lkl = lkl + log(pval*bw[ctr] + 1.); + } + //printf("loc %d %d %f %f %f %f\n", loc, msum, bw[0], eloc, pval, lkl); + *(pmapd + loc) = lkl; //log(lkl); //get_lkl_pkr(pval, bw, msum); + /* + lkl = 0; + for (ctr=0; ctr < msum; ctr ++) + { + lkl = lkl + bw[ctr]; + } + *(pmapd + loc) = lkl; + printf("%d %d %f\n", msum, loc, pmapd[loc]); + */ + }else{ + *(cmapd + loc) = 0.; + *(pmapd + loc) = 0.; + }; + }; + //printf("loop done\n"); + + Py_END_ALLOW_THREADS; + + free(bw); + + PyObject *res = Py_BuildValue("OO", cmap, pmap); + Py_DECREF(cmap); + Py_DECREF(pmap); + return res; +} + + static PyMethodDef PSFMethods[] = { {"solve_for_locations", solve_for_locations, METH_VARARGS, "get coordinates within pixel based on its coordinates"}, + {"solve_for_locations_eintp", solve_for_locations_eintp, METH_VARARGS, "compute likelihood using psf energy interpolation"}, + {"put_psf_on_img", put_psf_on, METH_VARARGS, "put psf as is on img for all cooreindates "}, {NULL, NULL, 0, NULL} }; diff --git a/rate_solvers.c b/rate_solvers.c index 4c451c1..ba90a34 100644 --- a/rate_solvers.c +++ b/rate_solvers.c @@ -288,7 +288,7 @@ double get_phc_solution_pkr(double r, double e, double *pk, int size) { rnew = lkl_rate_condition_pkr(r, pk, size)*r/e; //printf("rnew %d %f\n", i, rnew*e); - if ((fabs(rnew - r) < 1e-7) | (rnew*e < 0.001)) break; + if ((fabs(rnew/r - 1.) < 1e-7) | (rnew*e < 0.001)) break; //if ((fabs(rnew - r) < 1e-7) | (rnew*e < 1.)) break; r = rnew; }; diff --git a/source_detection2.py b/source_detection2.py new file mode 100644 index 0000000..64d8639 --- /dev/null +++ b/source_detection2.py @@ -0,0 +1,122 @@ +import asyncio +import numpy as np +from astropy.io import fits +import matplotlib.pyplot as plt +from astropy.wcs import WCS +import tqdm +from multiprocessing.pool import ThreadPool +from chan_psf import solve_for_locations, solve_for_locations_eintp + + +psfe = np.array([1.8, 1.9, 3.0, 4.0, 6.0, 7.0, 8.0, 9.0]) + +def prepare_psf(evt): + """ + find all unique psf for observation and load in single 3d data cuve + return data cube with events slices indexes + """ + u, ui = np.unique(evt["psf_cube"], return_inverse=True) + data = np.array([np.load(p[3:])[:, ::-1,::-1].copy() for p in u]) + return data, ui + +def select_xychunksize(wcs, halfpsfsize=36./3600.): + """ + get wcs and find wcs pixel size of psf reach + """ + sizex = int(abs(halfpsfsize/wcs.wcs.cdelt[1])) + 2 + sizey = int(abs(halfpsfsize/wcs.wcs.cdelt[0])) + 2 + print(sizex, sizey) + return sizex, sizey + +def read_wcs(h): + """ + read events wcs header + """ + w = WCS(naxis=2) + w.wcs.ctype = [h["TCTYP11"], h["TCTYP12"]] + w.wcs.crval = [h["TCRVL11"], h["TCRVL12"]] + w.wcs.cdelt = [h["TCDLT11"], h["TCDLT12"]] + w.wcs.crpix = [h["TCRPX11"], h["TCRPX12"]] + w = WCS(w.to_header()) + return w + + +def create_neighboring_blocks(x, y, sizex, sizey): + """ + schematically all sky is splitted on squares, which are approximatelly ~ 10 times greater then the psf + events for corresponding square are joined :: squer + diluttaion of psf reach + + coordinate system with events and all required coefficiets are fed to psf solver + current psf size is 25*0.5 arcsec (with up to \sqrt(2) factor in case of worst rolls -> 36'' + + """ + """ + event list already contains x and y for each event + """ + iix = (x//sizex + 0.5).astype(int) + iiy = (y//sizey + 0.5).astype(int) + isx, isy = np.mgrid[-1:2:1, -1:2:1] + oidx = np.repeat(np.arange(x.size), 9) + xyu, iixy, xyc = np.unique(np.array([np.repeat(iix, 9) + np.tile(isx.ravel(), x.size), + np.repeat(iiy, 9)+ np.tile(isy.ravel(), x.size)]), axis=1, return_counts=True, return_inverse=True) + + sord = np.argsort(iixy) + return oidx[sord], xyu, xyc + + +def make_srccount_and_detmap(emap, evt, h, wcs=None): + psfdata, ui = prepare_psf(evt) + if wcs is None: + wcs = read_wcs(h) + x, y = evt["x"], evt["y"] + else: + ewcs = read_wcs(h) + x, y = wcs.all_world2pix(ewcs.all_pix2world(np.array([x, y]).T, 0), 0).T + + eidx = np.searchsorted(psfe*1e3, evt["ENERGY"]) + eidx = np.maximum((evt["ENERGY"]/1000. - psfe[eidx])/(psfe[eidx + 1] - psfe[eidx]), 0.) + sizex, sizey = select_xychunksize(wcs) + iidx, xyu, cts = create_neighboring_blocks(x, y, sizex, sizey) + cc = np.zeros(cts.size + 1, int) + cc[1:] = np.cumsum(cts) + cmap, pmap = np.zeros(emap.shape, float), np.zeros(emap.shape, float) + #xe, ye, pk, roll, psfi = np.copy(evt["x"][iidx]), np.copy(evt["y"][iidx]), np.copy((evt["quant_eff"]/evt["bkg_model"])[iidx]), np.copy(evt["roll_pnt"][iidx]), np.copy(ui[iidx]) + xe = np.copy(x[iidx]).astype(float) + ye = np.copy(y[iidx]).astype(float) + ee = np.copy(eidx[iidx]).astype(float) + pk = np.copy(evt["src_spec"][iidx]/evt["bkg_spec"][iidx]).astype(float) + roll = np.copy(np.deg2rad(evt["roll_pnt"][iidx])).astype(float) + psfi = np.copy(ui[iidx]) + + yg, xg = np.mgrid[0:sizey:1, 0:sizex:1] + + def worker(ixys): + i, (xs, ys) = ixys + eloc = emap[ys*sizey:ys*sizey+sizey, xs*sizex:xs*sizex+sizex] + mask = eloc > 0. + xl = (xg[mask] + xs*sizex).astype(float) + yl = (yg[mask] + ys*sizey).astype(float) + ell = (eloc[mask]).astype(float) + if np.any(mask): + cr, pr = solve_for_locations_eintp(psfi[cc[i]:cc[i+1]], ee[cc[i]:cc[i + 1]], xe[cc[i]:cc[i+1]], ye[cc[i]:cc[i+1]], roll[cc[i]:cc[i+1]], pk[cc[i]:cc[i+1]], xl, yl, ell, psfdata) + else: + xl, yl, cr, pr = np.empty(0, int),np.empty(0, int),np.empty(0, float),np.empty(0, float) + return xl.astype(int), yl.astype(int), cr, pr + + + tpool = ThreadPool(8) + for xl, yl, cr, pr in tqdm.tqdm(tpool.imap_unordered(worker, enumerate(xyu.T)), total=xyu.shape[1]): + cmap[yl, xl] = cr + pmap[yl, xl] = pr + + return wcs, cmap, pmap + + +if __name__ == "__main__": + p1 = fits.open("test.fits") + #emap = fits.getdata("exp.map.gz") #np.full((8192, 8192), 10000.) + emap = fits.getdata("eR_spec_asp_0.fits.gz") #np.full((8192, 8192), 10000.) + + wcs, cmap, pmap = make_srccount_and_detmap(emap, p1[1].data, p1[1].header) + fits.HDUList([fits.PrimaryHDU(), fits.ImageHDU(pmap - cmap, header=p1[1].header), fits.ImageHDU(cmap, header=p1[1].header)]).writeto("tmap4.fits.gz", overwrite=True) + #fits.ImageHDU(data=pmap, header=wcs.to_header()).writeto("tmap4.fits.gz", overwrite=True)