Catalogue Fitting

Synference has a method specifically for fitting catalogues of sources - fit_catalogue.

This is designed to be a flexible and fast way to fit your full catalogue.

It can handle:

  1. Transforming observations to match the expected model features.

  2. Prior predictive checks/outlier detection to remove sources that are unlikely to be well modelled.

  3. Optional imputation of missing data.

  4. Rapid posterior inference using trained model.

  5. Optional SED recovery (see this notebook for more details).

  6. Returning structured results.

[1]:
import os

import numpy as np
from IPython.display import display
from synthesizer import get_grids_dir
from unyt import Jy

from synference import SBI_Fitter, load_unc_model_from_hdf5, test_data_dir

print(get_grids_dir())

available_grids = os.listdir(get_grids_dir())
if "test_grid.hdf5" not in available_grids:
    cmd = f"synthesizer-download --test-grids --destination {get_grids_dir()}"
    os.system(cmd)

library_path = os.path.join(get_grids_dir(), "test_grid.hdf5")
/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html
  from .autonotebook import tqdm as notebook_tqdm
/home/runner/.local/share/Synthesizer/grids

We’ll use a subset of the JADES spectroscopic catalogue used in the synference paper for this example.

[2]:
from astropy.table import Table

cat = Table.read(f"{test_data_dir}/jades_spec_catalogue_subset.fits")

The first step is the same as our SED recovery example - we will load our trained model and the noise model used to train the model. We need the noise model to transform our observations to match the model features.

[3]:
library_path = (
    f"{test_data_dir}/grid_BPASS_Chab_DenseBasis_SFH_0.01_z_14_logN_2.7_Calzetti_v3_multinode.hdf5"  # noqa: E501
)

fitter = SBI_Fitter.load_saved_model(
    model_file=f"{test_data_dir}", library_path=library_path, device="cpu"
)

nm_path = f"{test_data_dir}/BPASS_DenseBasis_v4_final_nsf_0_params_empirical_noise_models.h5"
noise_models = load_unc_model_from_hdf5(nm_path)

fitter.feature_array_flags["empirical_noise_models"] = noise_models
2026-05-01 18:11:35,331 | synference | INFO     | Loaded model from /home/runner/.local/share/Synthesizer/data/synference/BPASS_DenseBasis_v4_final_nsf_0_posterior.pkl.
2026-05-01 18:11:35,332 | synference | INFO     | Device: cpu
2026-05-01 18:11:35,378 | synference | WARNING  | IndexError when trying to set train/test arrays.

Now we need to explain our catalogue to synference. We create a ‘conversion_dict’ dictionary which maps the feature names to columns in the table. The expected column names for flux are the SVO format filter names, e.g. JWST/NIRCam.F070W, or just F070W if it is unambiguous. For flux errors it is the same, but with ‘unc_’ prefixed, e.g. unc_JWST/NIRCam.F070W or unc_F070W. Any other features (e.g. redshift) should be mapped to the column name in the catalogue.

In this case our catalogue is almost in the correct format, so we just need to convert the band names to include the facility and instrument. We do this by getting the band names from the fitted model (fitter.feature_names) and replacing the relevant parts of the strings.

We then create our conversion dictionary to map from e.g. ‘unc_F200W’ to ‘unc_JWST/NIRCam.F200W’.

[4]:
def band_to_instrument(band):
    """Band name to instrument mapping."""
    if band in ["F435W", "F606W", "F775W", "F814W", "F850LP"]:
        return f"HST/ACS_WFC.{band}"
    return f"JWST/NIRCam.{band}"


bands = [
    i.split(".")[-1]
    for i in fitter.feature_names
    if not (i.startswith("unc_") or i.startswith("redshift"))
]

print(bands)

conversion_dict = {band: band_to_instrument(band) for band in bands}
conversion_dict.update({f"unc_{band}": f"unc_{band_to_instrument(band)}" for band in bands})
conversion_dict["redshift"] = "redshift"

conversion_dict
['F435W', 'F606W', 'F775W', 'F814W', 'F850LP', 'F090W', 'F115W', 'F150W', 'F200W', 'F277W', 'F335M', 'F356W', 'F410M', 'F444W']
[4]:
{'F435W': 'HST/ACS_WFC.F435W',
 'F606W': 'HST/ACS_WFC.F606W',
 'F775W': 'HST/ACS_WFC.F775W',
 'F814W': 'HST/ACS_WFC.F814W',
 'F850LP': 'HST/ACS_WFC.F850LP',
 'F090W': 'JWST/NIRCam.F090W',
 'F115W': 'JWST/NIRCam.F115W',
 'F150W': 'JWST/NIRCam.F150W',
 'F200W': 'JWST/NIRCam.F200W',
 'F277W': 'JWST/NIRCam.F277W',
 'F335M': 'JWST/NIRCam.F335M',
 'F356W': 'JWST/NIRCam.F356W',
 'F410M': 'JWST/NIRCam.F410M',
 'F444W': 'JWST/NIRCam.F444W',
 'unc_F435W': 'unc_HST/ACS_WFC.F435W',
 'unc_F606W': 'unc_HST/ACS_WFC.F606W',
 'unc_F775W': 'unc_HST/ACS_WFC.F775W',
 'unc_F814W': 'unc_HST/ACS_WFC.F814W',
 'unc_F850LP': 'unc_HST/ACS_WFC.F850LP',
 'unc_F090W': 'unc_JWST/NIRCam.F090W',
 'unc_F115W': 'unc_JWST/NIRCam.F115W',
 'unc_F150W': 'unc_JWST/NIRCam.F150W',
 'unc_F200W': 'unc_JWST/NIRCam.F200W',
 'unc_F277W': 'unc_JWST/NIRCam.F277W',
 'unc_F335M': 'unc_JWST/NIRCam.F335M',
 'unc_F356W': 'unc_JWST/NIRCam.F356W',
 'unc_F410M': 'unc_JWST/NIRCam.F410M',
 'unc_F444W': 'unc_JWST/NIRCam.F444W',
 'redshift': 'redshift'}

Now we can run the inference. We specify the catalogue table, the conversion dictionary, the input flux units (Jy), and then we have some choices.

We can choose to:

  1. Run predictive checks to remove outliers.

  2. Impute or remove missing data.

  3. Run posterior inference, setting the number of posterior samples to draw.

  4. Recover SEDs for each source.

[5]:
fitter.recreate_simulator_from_library(
    override_library_path=library_path, override_grid_path="test_grid.hdf5"
)

post_tab = fitter.fit_catalogue(
    cat,
    columns_to_feature_names=conversion_dict,
    flux_units=Jy,
    check_out_of_distribution=False,
    recover_SEDs=False,
    missing_data_flag=np.nan,
    num_samples=300,
    append_to_input=False,
)
2026-05-01 18:11:35,427 | synference | INFO     | Overriding internal library name from provided file path.
2026-05-01 18:11:35,701 | synference | WARNING  | Failed to load cosmology from HDF5. Using Planck18 instead.
2026-05-01 18:11:48,163 | synference | INFO     | Updated filters: ['HST/ACS_WFC.F435W', 'HST/ACS_WFC.F606W', 'HST/ACS_WFC.F775W', 'HST/ACS_WFC.F814W', 'HST/ACS_WFC.F850LP', 'JWST/NIRCam.F090W', 'JWST/NIRCam.F115W', 'JWST/NIRCam.F150W', 'JWST/NIRCam.F200W', 'JWST/NIRCam.F277W', 'JWST/NIRCam.F335M', 'JWST/NIRCam.F356W', 'JWST/NIRCam.F410M', 'JWST/NIRCam.F444W']
2026-05-01 18:11:48,166 | synference | INFO     | Simulator recreated from library at /home/runner/.local/share/Synthesizer/data/synference/grid_BPASS_Chab_DenseBasis_SFH_0.01_z_14_logN_2.7_Calzetti_v3_multinode.hdf5.
2026-05-01 18:11:48,167 | synference | INFO     | Auto applying inverse log10 transform for log10_Av.
2026-05-01 18:11:48,167 | synference | INFO     | Auto applying inverse log10 transform for log10_mass_weighted_age.
2026-05-01 18:11:48,168 | synference | INFO     | Auto applying inverse log10 transform for log10_floor_sfr_10.
2026-05-01 18:11:48,169 | synference | INFO     | Adding Av to tau_v transform.
WARNING {'true_flux_units': Jy, 'out_units': 'AB'} arguments will have no effect with this model. Input must be in Jy.
WARNING {'true_flux_units': Jy, 'out_units': 'AB'} arguments will have no effect with this model. Input must be in Jy.
WARNING {'true_flux_units': Jy, 'out_units': 'AB'} arguments will have no effect with this model. Input must be in Jy.
WARNING {'true_flux_units': Jy, 'out_units': 'AB'} arguments will have no effect with this model. Input must be in Jy.
WARNING {'true_flux_units': Jy, 'out_units': 'AB'} arguments will have no effect with this model. Input must be in Jy.
WARNING {'true_flux_units': Jy, 'out_units': 'AB'} arguments will have no effect with this model. Input must be in Jy.
WARNING {'true_flux_units': Jy, 'out_units': 'AB'} arguments will have no effect with this model. Input must be in Jy.
WARNING {'true_flux_units': Jy, 'out_units': 'AB'} arguments will have no effect with this model. Input must be in Jy.
WARNING {'true_flux_units': Jy, 'out_units': 'AB'} arguments will have no effect with this model. Input must be in Jy.
WARNING {'true_flux_units': Jy, 'out_units': 'AB'} arguments will have no effect with this model. Input must be in Jy.
WARNING {'true_flux_units': Jy, 'out_units': 'AB'} arguments will have no effect with this model. Input must be in Jy.
WARNING {'true_flux_units': Jy, 'out_units': 'AB'} arguments will have no effect with this model. Input must be in Jy.
WARNING {'true_flux_units': Jy, 'out_units': 'AB'} arguments will have no effect with this model. Input must be in Jy.
WARNING {'true_flux_units': Jy, 'out_units': 'AB'} arguments will have no effect with this model. Input must be in Jy.
2026-05-01 18:11:48,179 | synference | INFO     | Removing 54 observations with missing data.
Sampling from posterior: 100%|██████████| 76/76 [00:04<00:00, 17.96it/s]
2026-05-01 18:11:52,415 | synference | INFO     | Obtained posterior samples.

We can look at the output table, which is also an astropy.table.Table.

[6]:
post_tab
[6]:
Table length=99
IDlog_mass_16log_mass_50log_mass_84log10metallicity_16log10metallicity_50log10metallicity_84log10_Av_16log10_Av_50log10_Av_84log_sfr_16log_sfr_50log_sfr_84sfh_quantile_25_16sfh_quantile_25_50sfh_quantile_25_84sfh_quantile_50_16sfh_quantile_50_50sfh_quantile_50_84sfh_quantile_75_16sfh_quantile_75_50sfh_quantile_75_84log10_mass_weighted_age_16log10_mass_weighted_age_50log10_mass_weighted_age_84log10_floor_sfr_10_16log10_floor_sfr_10_50log10_floor_sfr_10_84log_surviving_mass_16log_surviving_mass_50log_surviving_mass_84beta_16beta_50beta_84
int64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64
19.93958400726318410.10815429687510.23990779876709-3.9572409343719483-3.8117417097091675-3.5107469367980957-0.3456606268882751-0.16263077408075333-0.0572725157439708740.195498356819152870.54585418105125430.80472312211990360.054771153330802920.22755938023328780.47948365092277530.271905876398086550.53260433673858640.77575813770294190.59462989330291750.86053809523582460.96439511775970453.1783593177795413.39326846599578863.522296819686890.17328704714775090.55763489007949830.7802479386329659.7020880126953139.8486394882202159.964367599487305-1.4193619108200073-1.2767080664634705-1.1728471517562866
29.1548147201538099.3384871482849129.51364387512207-3.9008369636535645-3.7287739515304565-3.4822669982910157-2.9504598331451417-2.779890537261963-2.1422450733184815-1.1176093578338622-0.8014236390590668-0.63176970243453980.06789843976497650.227089636027812960.485234125852584850.254365661144256570.5407235920429230.80501307964324950.469491636753082250.80811813473701480.93384590625762943.3236070060729983.5253691673278813.683005542755127-1.1443159198760986-0.8328116238117218-0.7140940904617318.9018944549560559.0607819557189949.220348281860351-1.8988433027267455-1.768225908279419-1.5384234333038331
310.52973560333251910.65761709213256810.806660118103027-3.5730961990356445-3.0973167419433594-2.72304939270019550.084660743176937110.16487022489309310.220974360108375541.17576833248138431.5010306835174561.68366630554199230.08917331427335740.25382468104362490.52698569059371950.30874859809875490.62451341748237610.8261347293853760.81288078784942630.95492082834243770.983896439075472.99098880767822273.1537284851074223.3192794895172121.07830183506011971.45811337232589721.644016656875610310.31891479492187510.43211603164672910.565086174011231-0.6545642995834351-0.4562164694070816-0.28958198547363284
410.34416400909423710.4970040321350110.605947227478028-2.984836301803589-2.580446720123291-2.361547508239746-1.4467960691452026-0.6145035922527313-0.27229827880859375-1.4194354963302613-1.0233412384986877-0.62243102073669430.0242979308962821970.117625489830970760.29740194916725160.154325205087661730.3065616786479950.50793431282043460.305867544412612940.45683258771896360.59927327156066893.57382179260253933.66532683372497563.73384352684021-1.486201481819153-1.1051756143569946-0.768800618648529110.06028877258300710.20256900787353510.305483169555664-0.70770002126693720.306106179952621460.9208155298233032
510.5104137420654310.60337543487548810.676143836975097-3.9544841766357424-3.7747520208358765-3.345344514846802-0.7332632708549499-0.47819650173187256-0.3484096038341522-0.868891875743866-0.42782431840896606-0.160153483152389530.0229295145720243460.086961466819047930.245961183309555050.114592148065567010.258265316486358640.46118703246116640.214324617385864250.38115441799163820.59989975452423093.3371691894531253.4249193668365483.477188892364502-0.8681553077697753-0.44005441665649414-0.1779687213897705510.23573204040527410.32231903076171910.388777618408204-0.4891953158378601-0.2739820033311844-0.0966482001543045
68.697783050537118.9040832519531259.072546081542969-3.027278137207031-2.7472602128982544-2.614431829452515-2.5537643527984617-1.6312091946601868-0.8682891082763672-1.0009292697906493-0.882975846529007-0.7012058258056640.057385316938161850.183145813643932340.42614395976066590.238849425315856960.483001381158828740.72821922302246090.49207981348037720.75247469544410710.94633733034133923.52443547248840353.7083008289337163.8192360019683838-1.01308114528656-0.9079616665840149-0.79269651889801038.4351298141479498.6215310096740728.77653537750244-2.1178145694732664-2.0540117025375366-1.9221822834014892
78.2706259918212898.443805217742928.57195743560791-3.890875835418701-3.6433757543563843-3.399082736968994-2.4719399070739745-0.9833451509475708-0.47715588569641115-0.7157675504684448-0.5548348426818848-0.35875241756439210.0498336634039878850.156931146979331970.42279460906982420.218842947483062740.436737179756164550.70580780744552610.41858079314231870.67872071266174320.87888443231582653.0670145702362063.24203550815582283.337159433364868-0.7100361251831054-0.5775145888328552-0.386370192766189568.0436870193481458.1981816291809088.314944496154785-2.23298789024353-2.147029757499695-1.945000205039978
811.21285942077636711.32367038726806611.4198921585083-3.934935522079468-3.743446707725525-3.34980581283569330.0067398256994783880.091916646808385850.204316832423210140.229363063573837340.8893302679061891.69780363559722880.074623459279537220.28159873187541960.51723639488220220.310231819152832040.55513602495193480.76234831809997560.69307648181915280.79128706455230710.87699932098388673.0783197212219243.21824252605438233.33438340187072770.21839096307754520.87546795606613161.680266370773315410.97440826416015511.07363080978393611.157762069702148-0.20267665922641753-0.039262846112251280.11406765431165694
99.99510787963867210.1038737297058110.19513687133789-3.987198619842529-3.9449836015701294-3.7821510314941404-0.39735090017318725-0.20022757351398468-0.017654912620782857-0.335687490701675360.178019039332866670.58624558448791510.043741038739681250.143394671380519870.34487996339797970.180652680993080130.39323616027832030.65526600360870360.37268562316894530.63944247364997860.82191497802734373.26889407157897963.40081453323364263.480146913528442-0.37567999720573420.118206899613142010.53350557327270519.7360528945922869.832139968872079.916845397949219-1.18173020362854-1.0558088421821594-0.8812924289703369
......................................................................................................
908.5090282058715828.624843597412118.717207145690917-3.906067228317261-3.667941927909851-3.254606294631958-2.984197006225586-2.949751615524292-2.8277369022369383-0.027535172626376150.043207345530390740.10927103191614150.079510800838470470.22335620969533920.49936128020286560.44674908399581910.70168179273605350.85293578147888180.8223360157012940.9538599550724030.98507061004638672.7995705699920652.9632101058959963.105194454193115-0.0393963456153869560.035111425444483760.095289052426815038.3184606933593758.4229063987731938.499647598266602-2.3545788764953612-2.3428192138671875-2.3179873847961425
919.6887203979492189.7501111030578619.816822090148925-3.9633341789245606-3.896526575088501-3.7444027996063234-2.4114700031280516-1.5445653200149536-0.6405145835876466-1.6148673248291014-0.67069086432456970.0379527534544467640.124866779446601880.334098532795906070.5996396756172180.48654360294342040.72562733292579650.86587966680526730.89096143722534180.91613432765007020.94747399568557742.92904398918151853.05802381038665773.1877545166015624-1.6564894676208495-0.69684347510337830.023325474709272369.4713431167602549.5214643478393559.577912788391114-1.717333436012268-1.6516600251197815-1.5686198377609253
9210.21322731018066310.35208988189697310.500415229797364-2.6412713146209716-2.2973498106002808-1.9657570409774780.225032924413681030.283961549401283260.331866658926010141.28716066360473641.47209841012954711.63081327915191650.0482869854569435150.179588027298450470.38760883569717410.197983623743057250.41570797562599180.66046874523162850.3856466305255890.6575118303298950.8519568204879763.1490637302398683.2969726324081423.41101305007934561.26557877063751211.42943137884140011.55885757923126229.99976909637451310.1180500984191910.247216682434082-0.210206782221794120.0311919152736663820.2637383449077606
938.2703246688842788.416488170623788.522477493286132-3.977976999282837-3.901645064353943-3.7265852165222166-2.1078587150573727-1.2493868470191956-0.6279422855377198-1.7062082052230836-1.450003445148468-1.19904510498046870.0440460774302482640.149971812963485720.36622023701667780.162738502025604250.373990893363952640.65155327320098880.283868744373321550.57560157775878910.86673073053359983.3986310195922853.54340279102325443.63385272026062-1.693820958137512-1.4522681832313538-1.2246050405502328.00341712951668.1337080001831058.229930152893067-2.0076331520080566-1.9071319699287415-1.7365031480789184
948.8302397918701178.9609327316284189.080847778320312-2.684078426361084-2.217964768409729-2.119458999633789-0.3016149890422821-0.23844961822032928-0.194854422211647030.75845437049865730.89845326542854310.993960759639740.121156933903694160.33968925476074220.62191252470016480.421979916095733650.67467978596687320.86911376714706420.87646934747695930.97725892066955570.99284878492355342.40068479537963862.6123280525207522.79694949150085440.8194930434226990.91054573655128480.99407434701919558.6878224945068368.7998833656311048.910940895080566-1.7012621879577636-1.6158613562583923-1.5505807733535766
958.8644182968139649.0732150077819829.237068099975586-3.6159160041809084-3.1095324754714966-2.783223934173584-1.1834826755523682-0.5715051889419556-0.323266099691391-1.6315010070800782-1.175331711769104-0.94259842395782470.0537659208476543440.202576272189617160.475898445844650240.27894425868988040.53503146767616270.76864818572998050.53838572263717650.84008538722991940.95702759981155393.52903469085693373.7359935045242313.8529014015197753-1.6786646699905396-1.2243101000785828-1.01992951393127458.5944350433349618.7892041206359868.940436515808106-1.7577277517318726-1.4093816876411438-0.9586470198631287
9610.75117187510.8702898025512710.965835037231445-2.0962821292877196-1.868673324584961-1.7033158206939698-1.0801838874816894-0.42823173105716705-0.18155021846294403-1.307304449081421-1.0443772673606873-0.72278908014297480.0108419509604573260.031890850514173510.09857728809118270.052693466842174530.126383133232593540.22363453269004820.06667692154645920.15065290033817290.239783493280410763.720045633316043.77086091041564943.796512098312378-1.5195269012451171-1.304945468902588-1.000094408988952710.4506300354003910.56428909301757810.649251136779785-0.478325494527816750.054120363667607310.776248743534088
9710.82730491638183610.94704771041870111.045567741394043-3.7583420944213866-3.2069404125213623-2.7297824668884276-0.7947887682914734-0.29402290284633636-0.12604100346565247-1.1027163887023925-0.8602952659130096-0.61620980739593510.0107081507518887520.032713616266846660.088849570751190170.0497031664848327640.10754055529832840.194109173417091360.0616124461591243750.133646041154861450.2307601612806323.4526087570190433.489093303680423.513179311752319-1.1174827241897582-0.8771901726722717-0.624892764091491710.54222248077392610.65750980377197310.7496325683593761.29778130531311041.73348742723464972.1066622161865234
98nannannannannannannannannannannannannannannannannannannannannannannannannannannannannannannannannan
9910.62272529602050910.74443578720092810.857611045837402-1.8423300170898438-1.7279205322265625-1.6034236097335814-1.7683616399765014-1.1998807787895203-0.93817061901092522.3847012996673582.50097668170928962.5932009124755860.096040638387203230.26961791515350340.50021397590637210.35132215499877930.62105214595794680.81320147275924680.59094144105911260.8316675722599030.9691775774955752.41393469810485862.5547888278961182.68818793296813972.4071124649047852.494329094886782.569800767898559710.47379459381103610.5852265357971210.691821250915527-2.101516981124878-2.0340031385421753-1.9653551006317138

And we can plot the star-forming main sequence from the fitted catalogue.

[7]:
import matplotlib.pyplot as plt

plt.scatter(post_tab["log_mass_50"], post_tab["log_sfr_50"])
plt.xlabel("Stellar Mass (log M_sun)")
plt.ylabel("SFR (log M_sun/yr)")
[7]:
Text(0, 0.5, 'SFR (log M_sun/yr)')
../_images/posterior_inference_catalogue_fitting_13_1.png

You may notice some of rows and nans. This is because those rows had missing data that we chose not to impute, which can’t be handled by the SBI inference method.

We could set missing_data_mcmc to True to impute those missing values instead.

We can also recover and plot the SEDs, which we’ll do for the first few sources in the catalogue.

[8]:
post_tab, data = fitter.fit_catalogue(
    cat[:4],
    columns_to_feature_names=conversion_dict,
    flux_units=Jy,
    check_out_of_distribution=False,
    recover_SEDs=True,
    plot_SEDs=True,
    missing_data_flag=np.nan,
    num_samples=300,
    append_to_input=False,
)
WARNING {'true_flux_units': Jy, 'out_units': 'AB'} arguments will have no effect with this model. Input must be in Jy.
WARNING {'true_flux_units': Jy, 'out_units': 'AB'} arguments will have no effect with this model. Input must be in Jy.
WARNING {'true_flux_units': Jy, 'out_units': 'AB'} arguments will have no effect with this model. Input must be in Jy.
WARNING {'true_flux_units': Jy, 'out_units': 'AB'} arguments will have no effect with this model. Input must be in Jy.
WARNING {'true_flux_units': Jy, 'out_units': 'AB'} arguments will have no effect with this model. Input must be in Jy.
WARNING {'true_flux_units': Jy, 'out_units': 'AB'} arguments will have no effect with this model. Input must be in Jy.
WARNING {'true_flux_units': Jy, 'out_units': 'AB'} arguments will have no effect with this model. Input must be in Jy.
WARNING {'true_flux_units': Jy, 'out_units': 'AB'} arguments will have no effect with this model. Input must be in Jy.
WARNING {'true_flux_units': Jy, 'out_units': 'AB'} arguments will have no effect with this model. Input must be in Jy.
WARNING {'true_flux_units': Jy, 'out_units': 'AB'} arguments will have no effect with this model. Input must be in Jy.
WARNING {'true_flux_units': Jy, 'out_units': 'AB'} arguments will have no effect with this model. Input must be in Jy.
WARNING {'true_flux_units': Jy, 'out_units': 'AB'} arguments will have no effect with this model. Input must be in Jy.
WARNING {'true_flux_units': Jy, 'out_units': 'AB'} arguments will have no effect with this model. Input must be in Jy.
WARNING {'true_flux_units': Jy, 'out_units': 'AB'} arguments will have no effect with this model. Input must be in Jy.
Sampling from posterior: 100%|██████████| 4/4 [00:00<00:00, 19.38it/s]
2026-05-01 18:11:52,795 | synference | INFO     | Obtained posterior samples.

Recovering SEDs from posterior samples...:   0%|          | 0/4 [00:00<?, ?it/s]
Starting dense_basis. Failed to load FSPS, only GP-SFH module will be available.
2026-05-01 18:11:52,893 | synference | WARNING  | The following parameters are not used by the simulator: ['log10_Av', 'log_sfr', 'sfh_quantile_25', 'sfh_quantile_50', 'sfh_quantile_75', 'log10_mass_weighted_age', 'log10_floor_sfr_10', 'log_surviving_mass', 'beta', 'Av', 'mass_weighted_age', 'floor_sfr_10']
Recovering SEDs from posterior samples...:   0%|          | 0/4 [00:00<?, ?it/s]
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[8], line 1
----> 1 post_tab, data = fitter.fit_catalogue(
      2     cat[:4],
      3     columns_to_feature_names=conversion_dict,
      4     flux_units=Jy,
      5     check_out_of_distribution=False,
      6     recover_SEDs=True,
      7     plot_SEDs=True,
      8     missing_data_flag=np.nan,
      9     num_samples=300,
     10     append_to_input=False,
     11 )

File /opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/synference/sbi_runner.py:3347, in SBI_Fitter.fit_catalogue(self, observations, columns_to_feature_names, flux_units, missing_data_flag, quantiles, sample_method, sample_kwargs, num_samples, timeout_seconds_per_row, override_transformations, append_to_input, return_feature_array, recover_SEDs, plot_SEDs, check_out_of_distribution, simulator, outlier_methods, missing_data_mcmc, missing_data_mcmc_params, return_full_samples, log_times, use_temp_samples, **kwargs)
   3344     if "." not in param:
   3345         extra_parameters[param] = obs_i[list(self.feature_names).index(param)]
-> 3347 fnu_quantiles, wav, phot_fnu_draws, phot_wav, fig = self.recover_SED(
   3348     X_test=obs_i,
   3349     samples=samples_i,
   3350     num_samples=num_samples,
   3351     sample_method=sample_method,
   3352     sample_kwargs=sample_kwargs,
   3353     plot=plot_SEDs,
   3354     plot_name=f"{self.name}_SED_{pos}",
   3355     simulator=simulator,
   3356     extra_parameters=extra_parameters,
   3357     verbose=False,
   3358 )
   3359 try:
   3360     id = table["ID"][pos]

File /opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/synference/sbi_runner.py:5833, in SBI_Fitter.recover_SED(self, X_test, samples, num_samples, sample_method, sample_kwargs, posteriors, simulator, prior, plot, marginalized_parameters, extra_parameters, phot_unit, true_parameters, plot_name, plots_dir, sample_color, param_labels, plot_closest_draw_to, plot_sfh, plot_histograms, kde, save_plots, fig, ax, ax_sfh, verbose)
   5831 params.update(extra_parameters)
   5832 params.update(pass_in_observables)
-> 5833 output = simulator(params)
   5834 if i == 0:
   5835     phot_wav = output["photo_wav"]

File /opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/synference/library.py:6001, in GalaxySimulator.__call__(self, params)
   5999 def __call__(self, params):
   6000     """Call the simulator with parameters to get photometry."""
-> 6001     return self.simulate(params)

File /opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/synference/library.py:5737, in GalaxySimulator.simulate(self, params)
   5735 if "sfh" in self.output_type:
   5736     stars_sfh = stars.get_sfh()
-> 5737     stars_sfh = stars_sfh / np.diff(10 ** (self.grid.log10age), prepend=0) / yr
   5738     time = 10 ** (self.grid.log10age) * yr
   5739     time = time.to("Myr")

File /opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/synthesizer/grid.py:336, in Grid.__getattr__(self, name)
    333     return np.log10(self._axes_values[name[5:]])
    335 # If we get here, we don't have the attribute
--> 336 raise AttributeError(
    337     f"'{self.__class__.__name__}' object has no attribute '{name}'"
    338 )

AttributeError: 'Grid' object has no attribute 'log10age'

If you recover the SEDs then a nested dictionary with the SED posteriors are also returned. Initial key is the source index or ID, then within that there are keys for ‘wav’, ‘fnu_quantiles’, ‘wav’, ‘phot_wav’, ‘phot_fnu_draws’ and ‘fig’.

[9]:
data[1].keys()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[9], line 1
----> 1 data[1].keys()

NameError: name 'data' is not defined

Here are the SED plots:

[10]:
for key in data.keys():
    display(data[key]["fig"])
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[10], line 1
----> 1 for key in data.keys():
      2     display(data[key]["fig"])

NameError: name 'data' is not defined

We can check the feature array used for inference by setting return_feature_array=True.

[11]:
feature_array, mask = post_tab, data = fitter.fit_catalogue(
    cat,
    columns_to_feature_names=conversion_dict,
    flux_units=Jy,
    check_out_of_distribution=False,
    return_feature_array=True,
    missing_data_flag=np.nan,
    num_samples=300,
    append_to_input=False,
)

feature_array
WARNING {'true_flux_units': Jy, 'out_units': 'AB'} arguments will have no effect with this model. Input must be in Jy.
WARNING {'true_flux_units': Jy, 'out_units': 'AB'} arguments will have no effect with this model. Input must be in Jy.
WARNING {'true_flux_units': Jy, 'out_units': 'AB'} arguments will have no effect with this model. Input must be in Jy.
WARNING {'true_flux_units': Jy, 'out_units': 'AB'} arguments will have no effect with this model. Input must be in Jy.
WARNING {'true_flux_units': Jy, 'out_units': 'AB'} arguments will have no effect with this model. Input must be in Jy.
WARNING {'true_flux_units': Jy, 'out_units': 'AB'} arguments will have no effect with this model. Input must be in Jy.
WARNING {'true_flux_units': Jy, 'out_units': 'AB'} arguments will have no effect with this model. Input must be in Jy.
WARNING {'true_flux_units': Jy, 'out_units': 'AB'} arguments will have no effect with this model. Input must be in Jy.
WARNING {'true_flux_units': Jy, 'out_units': 'AB'} arguments will have no effect with this model. Input must be in Jy.
WARNING {'true_flux_units': Jy, 'out_units': 'AB'} arguments will have no effect with this model. Input must be in Jy.
WARNING {'true_flux_units': Jy, 'out_units': 'AB'} arguments will have no effect with this model. Input must be in Jy.
WARNING {'true_flux_units': Jy, 'out_units': 'AB'} arguments will have no effect with this model. Input must be in Jy.
WARNING {'true_flux_units': Jy, 'out_units': 'AB'} arguments will have no effect with this model. Input must be in Jy.
WARNING {'true_flux_units': Jy, 'out_units': 'AB'} arguments will have no effect with this model. Input must be in Jy.
2026-05-01 18:11:54,517 | synference | INFO     | Removing 54 observations with missing data.
[11]:
array([[2.4493113e+01, 2.4163765e+01, 2.3604200e+01, ..., 9.3294436e-04,
        8.9689222e-04, 1.0870000e+00],
       [2.5077990e+01, 2.4549706e+01, 2.3904078e+01, ..., 6.5881172e-03,
        7.1780500e-03, 6.9384003e-01],
       [2.4922672e+01, 2.4493999e+01, 2.3787994e+01, ..., 1.1117980e-03,
        8.8765030e-04, 1.5530000e+00],
       ...,
       [2.6450207e+01, 2.4133791e+01, 2.2739664e+01, ..., 3.6288964e-04,
        2.9280997e-04, 6.6799998e-01],
       [2.7736353e+01, 2.6571774e+01, 2.5916496e+01, ..., 4.7382890e-04,
        3.4990878e-04, 1.6912301e+00],
       [2.3816370e+01, 2.3439030e+01, 2.3062237e+01, ..., 6.9473550e-04,
        6.0235627e-04, 5.7800002e+00]], shape=(76, 29), dtype=float32)

We can also run prior predictive checks to identify outliers, by setting check_out_of_distribution=True. The available list of methods is any of those in PYOD (https://pyod.readthedocs.io/en/latest/). You can set them with the ‘outlier_methods’ argument as a list of strings.