zdmp - #82
Conversation
…bout to pull in Jordan's stuff
… CRAFT observations
…mise optical parameters
…en MCMC adding host galaxies
… and associated functionalityt and debugging of main zdm code, including protection against corner cases
…s to MCMC gen in combined path zdm analysis
…culating Pn. Still not ideal though
…d adding analysis for combined paper
profxj
left a comment
There was a problem hiding this comment.
This is a review of the full zdmp vs main diff. There's a lot of good restructuring here (the survey rewrite, figures.py split, states.py presets), but the sweep turned up a set of confirmed breakages — mostly stale calls against renamed/deleted code and copy-paste slips in the new likelihood code — that I think need fixing before merge. Line numbers refer to the branch head.
1. Crashes / wrong results in core paths (blocking)
- grid.py:1169 —
Grid.update()'s DMhalo branch still callsget_efficiency_from_wlist(DMlist, wlist, wplist, model=...), but the new signature is(wlist, plist, model=..., ...). Anygrid.update({'DMhalo': ...})(MCMC/cube scans with DMhalo free) raisesTypeError: got multiple values for argument 'model'. Same stale pattern inzdm/scripts/MCMC/est_pz.py:245andaverage_grids.py:257. - grid.py:436,439 —
calc_pdv()now accumulates with+=intoself.b_fractions/self.w_fractions, but they're only zeroed whenNoneor whenbeam_bis passed. Everygrid.update()therefore adds fractions on top of the old ones: rates ~double after the first update, triple after the second, corrupting every subsequent likelihood. The old code assigned per-slice onj==0. - grid.py:555 —
calc_rates()appliesself.sfr *= self.fzin place, so any update path that re-runscalc_rateswithoutset_evolution(e.g. host-DM param updates) applies the z-fraction correction repeatedly, suppressing rates byfz^N. - states.py:310-311, 378-379 —
lRmin/lRmaxare swapped in bothHoffmannRepeaters26Pn(lRmax=-4.51, lRmin=3.78) andHoffmannRepeaters26Prep(lRmax=-3.92, lRmin=2.47), giving Rmin >> Rmax and a negative repeater constant inrepeat_grid.calc_constant. - states.py:66 —
elif case == "HoffmannRepeaters26":never matches (the valid cases are...Pn/...Prep), so theelsebranch callsset_chime_scatand silently overwrites the scattering/width parameters thatset_fit_paramsjust set for those cases. - iteration.py:592 — in
calc_likelihoods_1Dthe penalty check isif Pn==0:—Pnis the boolean function argument, the computed probability isprobN. The penalty branch is unreachable and an underflowingprobNgoes tonp.log10(0) = -inf. - iteration.py:1318 —
flg_baddmis only assigned in thesigmaDMG==0 and sigmaHalo==0branch; with Galactic DM uncertainty enabled,if flg_baddm:raisesNameError. - iteration.py:~1270-1441 —
calc_likelihoods_2Dcontains two stitched-together copies of the p(z,DM) computation. The first (~90 lines) is computed and then discarded, except the stray re-interpolation at 1363-1366 overwrites the MW-uncertainty-weightedpvalsfrom 1303-1309, and the orphanedelse:at 1367 referencesizs1/dkzs1that only exist in theif pdmz:branch (NameErrorwhenpdmz=False). Deleting the first copy restores a single code path. - iteration.py:730-750 — the
ptauwblock:tomult[:,:inoztaulist]uses a list as a slice bound (TypeError); the result is namedtz_tomultbut lines 749-750 multiply by undefinedzt_tomult; and the interpolation terms reusedkis1/dktaus1wheredkis2/dktaus2belong, so weights don't sum to 1. (Samedkis1-for-dkis2copy-paste in the 2D block at ~1508-1518.) - iteration.py:154-155 —
get_joint_path_zdm_likelihoodscallsget_PATH_lls(..., return_all=True), butget_PATH_llshas no such parameter; its early return (emptyilist) also yields a single float that callers unpack as two values. - survey.py:1186-1192 —
adjust_widths: undefined nameDMin the smearing computation,self.WIDTHS(capital S) typo, and the chained fancy-index assignmentself.WIDTHs[ismear][bad] = ...writes to a temporary copy, so the bad-value floor never applies (NaNs at the**0.5). - survey.py:1244-1249 —
fix_coordinateswritesself.frbs[i,'Gb'] = ...on a DataFrame, which creates a junk tuple-named column instead of setting the cell (needs.loc[i,'Gb']), so missing coordinates are never filled; also line 1226 parses Gb strings withhr=True, scaling latitudes by 15x. - survey.py:1133 — intrinsic widths compute
WIDTH**2 - (tscale*TAU)**2over all FRBs before theTAU != -1mask is applied, so the -1 "missing" sentinel is squared in as if it were a real 1 ms scattering time, corruptingIWIDTHsfor every FRB without a tau measurement. - optical.py:473, 492 —
loudas_model.give_p_mr_mass/give_p_mr_sfrreference undefinedn_redshift_bins(should beself.nzbins):NameErroron every call. - optical.py:622-629 — duplicated condition
elif self.opstate.AbsPriorMeth==0:(should be==1) makes the method-1 branch unreachable; if reached it would also hit unboundAbsPrior. - optical.py:1779, 1791 —
SimplekAbsoluteMagsarray path testsnp.isscalar(Abs)(parameter isApp) and returnsnp.log10(ApparentMags)— both undefined names. - optical_numerics.py:~304 — in
calc_path_priorstheif imatch is None: ... continuecontinues the inner survey loop, not the FRB loop, so an FRB found in no survey falls through:nfittedis still incremented ands.DMEGs[imatch]is evaluated withimatch=None(or the previous FRB's stale survey), corrupting the priors instead of skipping. - MCMC.py:177 — the
try/except ValueError → -infguard around the likelihood evaluation is commented out, so any math error in an impossible parameter region now kills the whole emcee run instead of returning-inf(the comment above still says it should). - grid.py:958, 990 —
GenMCFRB: lowest-z-bin sample usesself.dz/0.5(= 2·dz; should be/2.), stretching low-z MC redshifts ~2.5x; top-DM-bin branch usesdDM(a cumulative-probability increment) where the DM bin widthself.ddmis intended. - grid.py:209 —
get_dm_coeffsclamps only the lower bound; an FRB with DMEG ≥ dmvals[-1] givesidms2 == ndmand anIndexErrorincalc_likelihoods_1D/2D(get_z_coeffshas the top-bin clamp; this one needs it too). - pcosmic.py:266 —
get_mean_DMcallsexit()(killing the interpreter) whenDMbar[0]/DMbar[1]deviates from 1/3 by > 0.01, but the deviation scales as ~0.09·dz, so legitimately coarse z-grids terminate the whole process (e.g. a batch MCMC job) with no catchable exception. Raise aValueErrorinstead — and the tolerance probably wants to scale with dz.
2. References to deleted code (decide: restore or delete the callers)
- setup.cfg:82-83 — console scripts
zdm_build_cubeandzdm_pzdmpoint atzdm/scripts/build_cube.py(deleted in this PR) andzdm/scripts/pzdm.py(doesn't exist); the new CLAUDE.md (lines 91-92) documents both. - zdm/analyze_cube.py:42-43 — calls
iteration.set_orders/set_cube_shape, deleted with the cube machinery; breaks all cube post-processing (slurp_cubeand the papers/ scripts that use it). - zdm/cube.py:155/167/187 — still invokes
it.cube_likelihoodsandprocess_pfile, both deleted. - zdm/scripts/run_slice_benchmark.py:11 —
from zdm import MCMC2, deleted on this branch (same dead import intests/deprecated_test_update.py:10); line 60 also passessig=toget_log_likelihood, which has no such kwarg. - zdm/MC_sample/mc.py:67 — new file calls
misc_functions.plot_grid_2, deleted in this same PR (figures.py only hasplot_grid). - zdm/tests/test_scat_methods.py:110 — calls
survey.geometric_lognormals, deleted from survey.py; the test suite fails. - zdm/misc_functions.py:770 —
width_teststill calls the removed module-levelsurvey.make_widths(s, g.state)(now aSurveymethod with a different signature).
3. Silent behavior changes worth an explicit decision
- survey.py:208 — width-method codes were renumbered: old
Wmethod=3(single-FRB exact width) is now4, and3is a new z-dependent mode. Existing configs carrying3silently change meaning. Suggest keeping the old numbering or erroring on the ambiguous value. - grid.py:130 —
wdistchanged from a bool to a None-sentinel test while the docstring still says bool; callers passingwdist=Falsenow get the enabled branch. Rename the parameter or test explicitly. - survey.py:606 — beam value
B == -1silently gets uniform weights across all bins (adjacent comment says "this is incorrect"). Prefer NaN/None + an explicit mask so unknown beam values fail loudly rather than flowing into PATH weights. - survey.py:1376 —
init_halo_coeffsflips the sign of the 116.72 Yamasaki–Totani coefficient with no comment — intentional?
4. Performance notes (non-blocking, but they're in the MCMC hot path)
optical_numerics.run_pathre-reads FRB JSON, the PATH candidate CSV, andload_std_priors()per FRB per posterior evaluation;calc_path_priorsalso rebuildsop.Field()each call. All parameter-independent — cache once keyed by TNS name.grid.calc_rates → construct_fznp.loads two .npy files and rebuilds an interp1d per grid construction;survey.process_dm_maskre-loads the DMMASK .npy perreinit. Both cacheable.grid.smear_zconvolves per-DM-column in a Python loop;scipy.ndimage.convolve1d(..., axis=0)(already imported) does it in one vectorized call.calc_pdvruns a full-gridnp.isnanscan per (beam, width) pair — gate behind a debug flag or check the final sum once.
5. Duplication / cleanup (non-blocking)
figures.py:633get_alevelsis a verbatim copy offind_Alevels26 lines below in the same file.optical.pySimple{k}{Apparent,Absolute}Magsare four copies of one distance-modulus computation (the brokenSimplekAbsoluteMagsabove is a symptom); one helper withk=0wrappers would remove ~120 lines and the latent crash.- Four near-identical bin/weight interpolation helpers (
survey.get_internal_coeffs,survey.get_w_coeffs,grid.get_dm_coeffs,grid.get_z_coeffs) with subtly different edge handling — one shared helper would make the convention testable once. MCMC.calc_log_posteriorcarries dead code in the emcee hot path: unusedwrappers/ident/sident, an unreachabledebug=Falseblock, and a shadowingimport multiprocessingre-import.
Review prepared with Claude Code: an 8-agent scan of the full diff, with findings verified against the branch and the highest-impact items manually spot-checked.
|
I have addressed the above. I only note things I have not responded to: survey.py:1133 — intrinsic widths compute WIDTH**2 - (tscale*TAU)**2 over all FRBs before the TAU != -1 mask is applied, so the -1 "missing" sentinel is squared in as if it were a real 1 ms scattering time, corrupting IWIDTHs for every FRB without a tau measurement. MCMC.py:177 — the try/except ValueError → -inf guard around the likelihood evaluation is commented out, so any math error in an impossible parameter region now kills the whole emcee run instead of returning -inf (the comment above still says it should). I can't for the life of me work out what: zdm/misc_functions.py:770 — width_test still calls the removed module-level survey.make_widths(s, g.state) (now a Survey method with a different signature). |
|
Remaining questions are:
from setup.cfg? Can we just delete zdm/cube.py and zdm/analyze_cube.py? |
|
On silent changes:
survey.py:1376 — init_halo_coeffs flips the sign of the 116.72 Yamasaki–Totani coefficient with no comment — intentional? |
|
Regarding duplication / performance: I'd like to ask Claude to implement these, if possible, as a test. Or otherwise, keep thee recommendations around for the future |
|
Correction to my review above: the item flagging |
|
OK, cut extra chaff, will wait for tests to complete, then merge |
Pull request to allow p(xopt,xrad) to be simultaneously estimated. Contains new features, and also a few bug fixes. Pulling in now to allow Claude to work its magic