|
| 1 | +""" |
| 2 | +Bidirectional parameter conversion between PyAutoGalaxy light profiles and the |
| 3 | +COOLEST standard's light profiles. |
| 4 | +
|
| 5 | +The PyAutoGalaxy ``Sersic`` evaluates its image on *eccentric* radii |
| 6 | +``sqrt(q) * sqrt(x^2 + (y/q)^2)`` (``geometry_profiles.py``), which is exactly |
| 7 | +the COOLEST intermediate-axis radius ``sqrt(q x^2 + y^2 / q)``, and its |
| 8 | +``intensity`` is the amplitude at ``effective_radius``. The Sersic mapping is |
| 9 | +therefore an identity on (I_eff, theta_eff, n) — only the centre ordering and |
| 10 | +the ellipticity / position-angle conventions change. |
| 11 | +""" |
| 12 | + |
| 13 | +from typing import Callable, Dict |
| 14 | + |
| 15 | +from autogalaxy import exc |
| 16 | +from autogalaxy.interop.coolest import conventions |
| 17 | +from autogalaxy.profiles.light.standard.sersic import Sersic |
| 18 | +from autogalaxy.profiles.light.standard.sersic import SersicSph |
| 19 | + |
| 20 | + |
| 21 | +def _sersic_dict_from(profile, **kwargs) -> Dict: |
| 22 | + q, phi = conventions.q_phi_from_ell_comps(ell_comps=profile.ell_comps) |
| 23 | + center_x, center_y = conventions.center_from_centre(centre=profile.centre) |
| 24 | + return { |
| 25 | + "type": "Sersic", |
| 26 | + "parameters": { |
| 27 | + "I_eff": float(profile.intensity), |
| 28 | + "theta_eff": float(profile.effective_radius), |
| 29 | + "n": float(profile.sersic_index), |
| 30 | + "q": q, |
| 31 | + "phi": phi, |
| 32 | + "center_x": center_x, |
| 33 | + "center_y": center_y, |
| 34 | + }, |
| 35 | + } |
| 36 | + |
| 37 | + |
| 38 | +def _sersic_from(parameters: Dict) -> Sersic: |
| 39 | + q = parameters["q"] |
| 40 | + centre = conventions.centre_from_center( |
| 41 | + center_x=parameters["center_x"], center_y=parameters["center_y"] |
| 42 | + ) |
| 43 | + if q == 1.0: |
| 44 | + return SersicSph( |
| 45 | + centre=centre, |
| 46 | + intensity=float(parameters["I_eff"]), |
| 47 | + effective_radius=float(parameters["theta_eff"]), |
| 48 | + sersic_index=float(parameters["n"]), |
| 49 | + ) |
| 50 | + return Sersic( |
| 51 | + centre=centre, |
| 52 | + ell_comps=conventions.ell_comps_from_q_phi(q=q, phi=parameters["phi"]), |
| 53 | + intensity=float(parameters["I_eff"]), |
| 54 | + effective_radius=float(parameters["theta_eff"]), |
| 55 | + sersic_index=float(parameters["n"]), |
| 56 | + ) |
| 57 | + |
| 58 | + |
| 59 | +_TO_COOLEST: Dict[type, Callable] = { |
| 60 | + Sersic: _sersic_dict_from, |
| 61 | + SersicSph: _sersic_dict_from, |
| 62 | +} |
| 63 | + |
| 64 | +_FROM_COOLEST: Dict[str, Callable] = { |
| 65 | + "Sersic": _sersic_from, |
| 66 | +} |
| 67 | + |
| 68 | + |
| 69 | +def coolest_dict_from_light(profile) -> Dict: |
| 70 | + """ |
| 71 | + Convert a PyAutoGalaxy light profile to its COOLEST representation, a dict |
| 72 | + ``{"type": <COOLEST profile name>, "parameters": {<name>: <float>}}`` with |
| 73 | + all parameters in COOLEST conventions. |
| 74 | +
|
| 75 | + Parameters |
| 76 | + ---------- |
| 77 | + profile |
| 78 | + The light profile to convert. Supported: ``Sersic`` / ``SersicSph``. |
| 79 | + """ |
| 80 | + try: |
| 81 | + to_coolest = _TO_COOLEST[type(profile)] |
| 82 | + except KeyError: |
| 83 | + raise exc.ProfileException( |
| 84 | + f"The light profile {type(profile).__name__} has no COOLEST " |
| 85 | + f"converter. Supported profiles: " |
| 86 | + f"{sorted(cls.__name__ for cls in _TO_COOLEST)}." |
| 87 | + ) |
| 88 | + return to_coolest(profile) |
| 89 | + |
| 90 | + |
| 91 | +def light_profile_from(profile_type: str, parameters: Dict): |
| 92 | + """ |
| 93 | + Build a PyAutoGalaxy light profile from a COOLEST profile type name and |
| 94 | + its parameter dict (in COOLEST conventions). |
| 95 | +
|
| 96 | + Parameters |
| 97 | + ---------- |
| 98 | + profile_type |
| 99 | + The COOLEST profile name, e.g. "Sersic". |
| 100 | + parameters |
| 101 | + The COOLEST parameters of the profile, e.g. point-estimate values read |
| 102 | + from a COOLEST template file. |
| 103 | + """ |
| 104 | + try: |
| 105 | + from_coolest = _FROM_COOLEST[profile_type] |
| 106 | + except KeyError: |
| 107 | + raise exc.ProfileException( |
| 108 | + f"The COOLEST light profile '{profile_type}' has no PyAutoGalaxy " |
| 109 | + f"converter. Supported: {sorted(_FROM_COOLEST)}." |
| 110 | + ) |
| 111 | + return from_coolest(parameters) |
0 commit comments