From acb27f4a5664bca82b87023e50c6082702ed9f4b Mon Sep 17 00:00:00 2001 From: Abhimanyu Susobhanan Date: Mon, 21 Aug 2023 10:55:39 -0500 Subject: [PATCH 01/25] publish.py --- src/pint/output/publish.py | 205 +++++++++++++++++++++++++++++++++++++ 1 file changed, 205 insertions(+) create mode 100644 src/pint/output/publish.py diff --git a/src/pint/output/publish.py b/src/pint/output/publish.py new file mode 100644 index 000000000..0aa3a7940 --- /dev/null +++ b/src/pint/output/publish.py @@ -0,0 +1,205 @@ +from pint.models import TimingModel +from pint.models.parameter import ( + AngleParameter, + MJDParameter, + boolParameter, + floatParameter, + intParameter, + maskParameter, + prefixParameter, + strParameter, +) +from pint.toa import TOAs +from pint.residuals import Residuals +from io import StringIO +import numpy as np +from uncertainties import ufloat + + +def publish(model: TimingModel, toas: TOAs): + psrname = model.PSR.value + nfree = len(model.free_params) + ephem = model.EPHEM.value + clock = model.CLOCK.value + timeeph = model.TIMEEPH.value + units = model.UNITS.value + + mjds = toas.get_mjds() + mjd_start, mjd_end = int(min(mjds.value)), int(max(mjds.value)) + data_span_yr = (mjd_end - mjd_start) / 365.25 + ntoas = len(toas) + + toa_type = "Wideband" if toas.is_wideband() else "Narrowband" + fit_method = ( + "GLS" + if np.any([nc.introduces_correlated_errors for nc in model.NoiseComponent_list]) + else "WLS" + ) + + res = Residuals(toas, model) + rms_res = res.calc_time_resids().to("us").value.std() + chi2 = res.chi2 + chi2_red = res.chi2_reduced + + exclude_params = [ + "START", + "FINISH", + "NTOA", + "CHI2", + "DMDATA", + "PSR", + "EPHEM", + "CLOCK", + "UNITS", + "TIMEEPH", + "T2CMETHOD", + "DILATEFREQ", + "INFO", + "ECL", + ] + + with StringIO("w") as tex: + tex.write("\\documentclass{article}\n") + tex.write("\\begin{document}\n") + + tex.write("\\begin{table}\n") + tex.write("\\caption{Parameters for PSR %s}\n" % psrname) + tex.write("\\begin{tabular}{ll}\n") + tex.write("\\hline\\hline\n") + tex.write("\\multicolumn{2}{c}{Dataset and Fit summary}\\\\ \n") + tex.write("\\hline\n") + tex.write("Pulsar name \\dotfill & %s \\\\ \n" % psrname) + tex.write( + "MJD range \\dotfill & %d---%d \\\\ \n" + % (mjd_start, mjd_end) + ) + tex.write( + "Data span (yr) \\dotfill & %.2f \\\\ \n" % data_span_yr + ) + tex.write("Number of TOAs \\dotfill & %d \\\\ \n" % ntoas) + tex.write("Number of free parameters \\dotfill & %d \\\\ \n" % nfree) + tex.write("TOA paradigm \\dotfill & %s \\\\ \n" % toa_type) + tex.write( + "Fitting method \\dotfill & %s \\\\ \n" % fit_method + ) + tex.write("Solar system ephemeris \\dotfill & %s \\\\ \n" % ephem) + tex.write("Timescale \\dotfill & %s \\\\ \n" % clock) + tex.write("Time unit \\dotfill & %s \\\\ \n" % units) + tex.write("Time ephemeris \\dotfill & %s \\\\ \n" % timeeph) + tex.write("RMS TOA residuals ($\\mu s$) \\dotfill & %.2f \\\\ \n" % rms_res) + tex.write("chi2 \\dotfill & %.2f \\\\ \n" % chi2) + tex.write("Reduced chi2 \\dotfill & %.2f \\\\ \n" % chi2_red) + tex.write("\\hline\n") + + tex.write("\multicolumn{2}{c}{Measured Quantities} \\\\ \n") + for fp in model.free_params: + param = getattr(model, fp) + if isinstance(param, MJDParameter): + uf = ufloat(param.value, param.uncertainty_value) + tex.write( + "%s, %s (%s)\dotfill & %s \\\\ \n" + % (param.name, param.description, str(param.units), f"{uf:.1uS}") + ) + elif isinstance(param, maskParameter): + tex.write( + "%s %s %s, %s (%s)\dotfill & %s \\\\ \n" + % ( + param.prefix, + param.key, + " ".join(param.key_value), + param.description, + str(param.units), + f"{param.as_ufloat():.1uS}", + ) + ) + else: + tex.write( + "%s, %s (%s)\dotfill & %s \\\\ \n" + % ( + param.name, + param.description, + str(param.units), + f"{param.as_ufloat():.1uS}", + ) + ) + tex.write("\\hline\n") + + tex.write("\multicolumn{2}{c}{Set Quantities} \\\\ \n") + tex.write("\\hline\n") + for p in model.params: + param = getattr(model, p) + if param.value is not None and param.frozen and p not in exclude_params: + if isinstance(param, maskParameter): + tex.write( + "%s %s %s, %s (%s)\dotfill & %f \\\\ \n" + % ( + param.prefix, + param.key, + " ".join(param.key_value), + param.description, + str(param.units), + param.value, + ) + ) + elif isinstance( + param, + (floatParameter, AngleParameter, MJDParameter, prefixParameter), + ): + tex.write( + "%s, %s (%s)\dotfill & %f \\\\ \n" + % (param.name, param.description, str(param.units), param.value) + ) + elif isinstance(param, MJDParameter): + tex.write( + "%s, %s (%s)\dotfill & %f \\\\ \n" + % (param.name, param.description, str(param.units), param.value) + ) + elif isinstance(param, strParameter): + tex.write( + "%s, %s \dotfill & %s \\\\ \n" + % (param.name, param.description, param.value) + ) + elif isinstance(param, boolParameter): + tex.write( + "%s, %s (Y/N)\dotfill & %s \\\\ \n" + % (param.name, param.description, "Y" if param.value else "N") + ) + elif isinstance(param, intParameter): + tex.write( + "%s, %s \dotfill & %d \\\\ \n" + % (param.name, param.description, param.value) + ) + + # tex.write("%s, %s (%s)\dotfill & %f \\\\ \n" % (param.name, param.description, str(param.units), param.value)) + + # Epoch of frequency determination (MJD)\dotfill & 53750 \\ + # Epoch of position determination (MJD)\dotfill & 53750 \\ + # Epoch of dispersion measure determination (MJD)\dotfill & 53750 \\ + # NE_SW (cm^-3)\dotfill & 0 \\ + # \hline + # \multicolumn{2}{c}{Derived Quantities} \\ + # \hline + # $\log_{10}$(Characteristic age, yr) \dotfill & 8.92 \\ + # $\log_{10}$(Surface magnetic field strength, G) \dotfill & 9.36 \\ + # $\log_{10}$(Edot, ergs/s) \dotfill & 33.46 \\ + # \hline + # \multicolumn{2}{c}{Assumptions} \\ + # \hline + # Clock correction procedure\dotfill & TT(BIPM2019) \\ + # Solar system ephemeris model\dotfill & DE421 \\ + # Binary model\dotfill & NONE \\ + # TDB units (tempo1 mode)\dotfill & Y \\ + # FB90 time ephemeris (tempo1 mode)\dotfill & Y \\ + # Shapiro delay due to planets\dotfill & N \\ + # Tropospheric delay\dotfill & N \\ + # Dilate frequency\dotfill & N \\ + # Electron density at 1 AU (cm$^{-3}$)\dotfill & 0.00 \\ + # Model version number\dotfill & 2.00 \\ + tex.write("\\hline\n") + tex.write("\\end{tabular}\n") + tex.write("\\end{table}\n") + tex.write("\\end{document}\n") + + output = tex.getvalue() + + return output From 0a3ca915cd6c0f8e32b1cde6464078c2e26fbe6c Mon Sep 17 00:00:00 2001 From: Abhimanyu Susobhanan Date: Mon, 21 Aug 2023 10:56:04 -0500 Subject: [PATCH 02/25] param desc --- src/pint/models/spindown.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pint/models/spindown.py b/src/pint/models/spindown.py index 78bbba8cf..ff9972968 100644 --- a/src/pint/models/spindown.py +++ b/src/pint/models/spindown.py @@ -108,7 +108,7 @@ def F_terms(self): def F_description(self, n): """Template function for description""" - return "Spin-frequency %d derivative" % n # if n else "Spin-frequency" + return "Spin-frequency derivative %d" % n if n > 0 else "Spin-frequency" def F_unit(self, n): """Template function for unit""" From 24c65a77695abbd4fc699fae09d5dbb03324f2dc Mon Sep 17 00:00:00 2001 From: Abhimanyu Susobhanan Date: Mon, 21 Aug 2023 10:56:36 -0500 Subject: [PATCH 03/25] as_ufloat --- src/pint/models/parameter.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/pint/models/parameter.py b/src/pint/models/parameter.py index 8a14e0106..31c43107c 100644 --- a/src/pint/models/parameter.py +++ b/src/pint/models/parameter.py @@ -1297,6 +1297,32 @@ def _print_uncertainty(self, unc): angle_arcsec /= 15.0 return angle_arcsec.to_string(decimal=True, precision=20) + def as_ufloat(self, units=None): + """Return the parameter as a :class:`uncertainties.ufloat` + + Will cast to the specified units, or the default + If the uncertainty is not set will be returned as 0 + + Parameters + ---------- + units : astropy.units.core.Unit, optional + Units to cast the value + + Returns + ------- + uncertainties.ufloat + + Notes + ----- + Currently :class:`~uncertainties.ufloat` does not support double precision values, + so some precision may be lost. + """ + if units is None: + units = self.units + value = self.quantity.to_value(units) if self.quantity is not None else 0 + error = self.uncertainty.to_value(units) if self.uncertainty is not None else 0 + return ufloat(value, error) + class prefixParameter: """Families of parameters identified by a prefix like ``DMX_0123``. From ea1021e0d336a92c73f5f3e9bae8fda8b2b35aa4 Mon Sep 17 00:00:00 2001 From: Abhimanyu Susobhanan Date: Mon, 21 Aug 2023 11:12:07 -0500 Subject: [PATCH 04/25] options --- src/pint/output/publish.py | 101 +++++++++++++++++++++++++------------ 1 file changed, 70 insertions(+), 31 deletions(-) diff --git a/src/pint/output/publish.py b/src/pint/output/publish.py index 0aa3a7940..36c6372a2 100644 --- a/src/pint/output/publish.py +++ b/src/pint/output/publish.py @@ -1,4 +1,5 @@ from pint.models import TimingModel +from pint.models.noise_model import NoiseComponent from pint.models.parameter import ( AngleParameter, MJDParameter, @@ -16,7 +17,14 @@ from uncertainties import ufloat -def publish(model: TimingModel, toas: TOAs): +def publish( + model: TimingModel, + toas: TOAs, + include_dmx=False, + include_noise=False, + include_jumps=True, + include_zeros=False, +): psrname = model.PSR.value nfree = len(model.free_params) ephem = model.EPHEM.value @@ -58,6 +66,12 @@ def publish(model: TimingModel, toas: TOAs): "ECL", ] + exclude_prefixes = [] + if not include_dmx: + exclude_prefixes.append("DMX") + if not include_jumps: + exclude_prefixes.append("JUMP") + with StringIO("w") as tex: tex.write("\\documentclass{article}\n") tex.write("\\begin{document}\n") @@ -93,42 +107,69 @@ def publish(model: TimingModel, toas: TOAs): tex.write("\multicolumn{2}{c}{Measured Quantities} \\\\ \n") for fp in model.free_params: - param = getattr(model, fp) - if isinstance(param, MJDParameter): - uf = ufloat(param.value, param.uncertainty_value) - tex.write( - "%s, %s (%s)\dotfill & %s \\\\ \n" - % (param.name, param.description, str(param.units), f"{uf:.1uS}") - ) - elif isinstance(param, maskParameter): - tex.write( - "%s %s %s, %s (%s)\dotfill & %s \\\\ \n" - % ( - param.prefix, - param.key, - " ".join(param.key_value), - param.description, - str(param.units), - f"{param.as_ufloat():.1uS}", + if fp not in exclude_params and all( + [not fp.startswith(pre) for pre in exclude_prefixes] + ): + param = getattr(model, fp) + + if isinstance(param._parent, NoiseComponent) and not include_noise: + continue + + if param.value == 0 and not include_zeros: + continue + + if isinstance(param, MJDParameter): + uf = ufloat(param.value, param.uncertainty_value) + tex.write( + "%s, %s (%s)\dotfill & %s \\\\ \n" + % ( + param.name, + param.description, + str(param.units), + f"{uf:.1uS}", + ) ) - ) - else: - tex.write( - "%s, %s (%s)\dotfill & %s \\\\ \n" - % ( - param.name, - param.description, - str(param.units), - f"{param.as_ufloat():.1uS}", + elif isinstance(param, maskParameter): + tex.write( + "%s %s %s, %s (%s)\dotfill & %s \\\\ \n" + % ( + param.prefix, + param.key, + " ".join(param.key_value), + param.description, + str(param.units), + f"{param.as_ufloat():.1uS}", + ) + ) + else: + tex.write( + "%s, %s (%s)\dotfill & %s \\\\ \n" + % ( + param.name, + param.description, + str(param.units), + f"{param.as_ufloat():.1uS}", + ) ) - ) tex.write("\\hline\n") tex.write("\multicolumn{2}{c}{Set Quantities} \\\\ \n") tex.write("\\hline\n") for p in model.params: param = getattr(model, p) - if param.value is not None and param.frozen and p not in exclude_params: + + if isinstance(param._parent, NoiseComponent) and not include_noise: + continue + + if param.value == 0 and not include_zeros: + continue + + if ( + param.value is not None + and param.frozen + and p not in exclude_params + and all([not p.startswith(pre) for pre in exclude_prefixes]) + ): if isinstance(param, maskParameter): tex.write( "%s %s %s, %s (%s)\dotfill & %f \\\\ \n" @@ -170,8 +211,6 @@ def publish(model: TimingModel, toas: TOAs): % (param.name, param.description, param.value) ) - # tex.write("%s, %s (%s)\dotfill & %f \\\\ \n" % (param.name, param.description, str(param.units), param.value)) - # Epoch of frequency determination (MJD)\dotfill & 53750 \\ # Epoch of position determination (MJD)\dotfill & 53750 \\ # Epoch of dispersion measure determination (MJD)\dotfill & 53750 \\ From be4ce42dd9c0408ea5b89b81603b63c391775c14 Mon Sep 17 00:00:00 2001 From: Abhimanyu Susobhanan Date: Mon, 21 Aug 2023 11:38:41 -0500 Subject: [PATCH 05/25] fix descriptions --- src/pint/models/absolute_phase.py | 8 +++++--- src/pint/models/frequency_dependent.py | 2 +- src/pint/models/jump.py | 2 +- src/pint/models/noise_model.py | 16 ++++------------ src/pint/models/pulsar_binary.py | 2 +- src/pint/models/timing_model.py | 5 ++++- 6 files changed, 16 insertions(+), 19 deletions(-) diff --git a/src/pint/models/absolute_phase.py b/src/pint/models/absolute_phase.py index 9d319e2eb..940bea474 100644 --- a/src/pint/models/absolute_phase.py +++ b/src/pint/models/absolute_phase.py @@ -30,19 +30,21 @@ def __init__(self): super().__init__() self.add_param( MJDParameter( - name="TZRMJD", description="Epoch of the zero phase.", time_scale="utc" + name="TZRMJD", + description="Epoch of the zero phase TOA.", + time_scale="utc", ) ) self.add_param( strParameter( - name="TZRSITE", description="Observatory of the zero phase measured." + name="TZRSITE", description="Observatory of the zero phase TOA." ) ) self.add_param( floatParameter( name="TZRFRQ", units=u.MHz, - description="The frequency of the zero phase measured.", + description="The frequency of the zero phase TOA.", ) ) self.tz_cache = None diff --git a/src/pint/models/frequency_dependent.py b/src/pint/models/frequency_dependent.py index c78359cd0..7e295a615 100644 --- a/src/pint/models/frequency_dependent.py +++ b/src/pint/models/frequency_dependent.py @@ -36,7 +36,7 @@ def __init__(self): name="FD1", units="second", value=0.0, - description="Coefficient of delay as a polynomial function of log-frequency", + description="Coefficient of frequency-dependent delay", # descriptionTplt=lambda x: ( # "%d term of frequency" " dependent coefficients" % x # ), diff --git a/src/pint/models/jump.py b/src/pint/models/jump.py index 17e231d25..b133040d2 100644 --- a/src/pint/models/jump.py +++ b/src/pint/models/jump.py @@ -93,7 +93,7 @@ def __init__(self): maskParameter( name="JUMP", units="second", - description="Amount to jump the selected TOAs by.", + description="Phase jump for selection.", ) ) self.phase_funcs_component += [self.jump_phase] diff --git a/src/pint/models/noise_model.py b/src/pint/models/noise_model.py index 02a5d291a..b0a887961 100644 --- a/src/pint/models/noise_model.py +++ b/src/pint/models/noise_model.py @@ -58,8 +58,7 @@ def __init__( name="EFAC", units="", aliases=["T2EFAC", "TNEF"], - description="A multiplication factor on" - " the measured TOA uncertainties,", + description="A multiplication factor for the measured TOA uncertainties,", ) ) @@ -68,9 +67,7 @@ def __init__( name="EQUAD", units="us", aliases=["T2EQUAD"], - description="An error term added in " - "quadrature to the scaled (by" - " EFAC) TOA uncertainty.", + description="An error term added in quadrature to the TOA uncertainty.", ) ) @@ -78,10 +75,7 @@ def __init__( maskParameter( name="TNEQ", units=u.LogUnit(physical_unit=u.second), - description="An error term added in " - "quadrature to the scaled (by" - " EFAC) TOA uncertainty in " - " the unit of log10(second).", + description="An log10-scale error term added in quadrature to the TOA uncertainty", ) ) self.covariance_matrix_funcs += [self.sigma_scaled_cov_matrix] @@ -307,9 +301,7 @@ def __init__( name="ECORR", units="us", aliases=["TNECORR"], - description="An error term added that" - " correlated all TOAs in an" - " observing epoch.", + description="An error term that correlates all TOAs in an observing epoch.", ) ) diff --git a/src/pint/models/pulsar_binary.py b/src/pint/models/pulsar_binary.py index 5e32079ad..c5089ab7d 100644 --- a/src/pint/models/pulsar_binary.py +++ b/src/pint/models/pulsar_binary.py @@ -158,7 +158,7 @@ def __init__(self): floatParameter( name="M2", units=u.M_sun, - description="Mass of companion in the unit Sun mass", + description="Companion mass", ) ) self.add_param( diff --git a/src/pint/models/timing_model.py b/src/pint/models/timing_model.py index 1bd05bfa2..dd47c38d4 100644 --- a/src/pint/models/timing_model.py +++ b/src/pint/models/timing_model.py @@ -308,7 +308,7 @@ def __init__(self, name="", components=[]): self.add_param_from_top( strParameter( name="BINARY", - description="The Pulsar System/Binary model to use.", + description="Pulsar System/Binary model", value=None, ), "", @@ -2909,6 +2909,9 @@ def add_param(self, param, deriv_func=None, setup=False): param.name = prefix + str(idx) param.index = idx + if hasattr(self, f"{prefix}1"): + param.description = getattr(self, f"{prefix}1").description + # A more general check if param.name in self.params: exist_par = getattr(self, param.name) From a6280d22e07a57e7223a40855e4a6d29d8d0696d Mon Sep 17 00:00:00 2001 From: Abhimanyu Susobhanan Date: Mon, 21 Aug 2023 13:01:04 -0500 Subject: [PATCH 06/25] exclude components --- src/pint/models/noise_model.py | 2 +- src/pint/models/parameter.py | 12 ++ src/pint/output/publish.py | 307 +++++++++++++++++---------------- 3 files changed, 167 insertions(+), 154 deletions(-) diff --git a/src/pint/models/noise_model.py b/src/pint/models/noise_model.py index b0a887961..455e00230 100644 --- a/src/pint/models/noise_model.py +++ b/src/pint/models/noise_model.py @@ -75,7 +75,7 @@ def __init__( maskParameter( name="TNEQ", units=u.LogUnit(physical_unit=u.second), - description="An log10-scale error term added in quadrature to the TOA uncertainty", + description="A log10-scale error term added in quadrature to the TOA uncertainty", ) ) self.covariance_matrix_funcs += [self.sigma_scaled_cov_matrix] diff --git a/src/pint/models/parameter.py b/src/pint/models/parameter.py index 31c43107c..2b1dd846f 100644 --- a/src/pint/models/parameter.py +++ b/src/pint/models/parameter.py @@ -1163,6 +1163,18 @@ def as_ufloats(self): error = self.uncertainty.to_value(u.d) if self.uncertainty is not None else 0 return ufloat(value1, 0), ufloat(value2, error) + def as_ufloat(self): + """Return the parameter as a :class:`uncertainties.ufloat` + value. + + If the uncertainty is not set will be returned as 0 + + Returns + ------- + uncertainties.ufloat + """ + return ufloat(self.value, self.uncertainty_value) + class AngleParameter(Parameter): """Parameter in angle units. diff --git a/src/pint/output/publish.py b/src/pint/output/publish.py index 36c6372a2..bab7c960f 100644 --- a/src/pint/output/publish.py +++ b/src/pint/output/publish.py @@ -1,20 +1,46 @@ -from pint.models import TimingModel +from pint.models import ( + TimingModel, + DispersionDMX, + FD, + Glitch, + PhaseJump, + SolarWindDispersionX, +) +from pint.models.absolute_phase import AbsPhase from pint.models.noise_model import NoiseComponent from pint.models.parameter import ( - AngleParameter, - MJDParameter, + Parameter, boolParameter, - floatParameter, intParameter, maskParameter, - prefixParameter, strParameter, ) from pint.toa import TOAs from pint.residuals import Residuals from io import StringIO import numpy as np -from uncertainties import ufloat + + +def publish_param_value(param: Parameter): + if isinstance(param, boolParameter): + return "Y" if param.value else "N" + elif isinstance(param, strParameter): + return param.value + elif isinstance(param, intParameter): + return str(param.value) + else: + return f"{param.as_ufloat():.1uS}" if not param.frozen else f"{param.value:f}" + + +def publish_param_unit(param: Parameter): + return "" if param.units == "" or param.units is None else f" ({param.units})" + + +def publish_param(param): + if isinstance(param, maskParameter): + return f"{param.prefix} {param.key} {' '.join(param.key_value)}, {param.description}{publish_param_unit(param)}\dotfill & {publish_param_value(param)} \\\\ \n" + else: + return f"{param.name}, {param.description}{publish_param_unit(param)}\dotfill & {publish_param_value(param)} \\\\ \n" def publish( @@ -22,22 +48,17 @@ def publish( toas: TOAs, include_dmx=False, include_noise=False, - include_jumps=True, + include_jumps=False, include_zeros=False, + include_fd=False, + include_glitches=False, + include_swx=False, + include_tzr=False, ): - psrname = model.PSR.value - nfree = len(model.free_params) - ephem = model.EPHEM.value - clock = model.CLOCK.value - timeeph = model.TIMEEPH.value - units = model.UNITS.value - mjds = toas.get_mjds() mjd_start, mjd_end = int(min(mjds.value)), int(max(mjds.value)) data_span_yr = (mjd_end - mjd_start) / 365.25 - ntoas = len(toas) - toa_type = "Wideband" if toas.is_wideband() else "Narrowband" fit_method = ( "GLS" if np.any([nc.introduces_correlated_errors for nc in model.NoiseComponent_list]) @@ -45,9 +66,6 @@ def publish( ) res = Residuals(toas, model) - rms_res = res.calc_time_resids().to("us").value.std() - chi2 = res.chi2 - chi2_red = res.chi2_reduced exclude_params = [ "START", @@ -64,93 +82,141 @@ def publish( "DILATEFREQ", "INFO", "ECL", + "BINARY", ] - exclude_prefixes = [] + exclude_components = [] if not include_dmx: - exclude_prefixes.append("DMX") + exclude_components.append(DispersionDMX) if not include_jumps: - exclude_prefixes.append("JUMP") + exclude_components.append(PhaseJump) + if not include_fd: + exclude_components.append(FD) + if not include_noise: + exclude_components.append(NoiseComponent) + if not include_glitches: + exclude_components.append(Glitch) + if not include_swx: + exclude_components.append(SolarWindDispersionX) + if not include_tzr: + exclude_components.append(AbsPhase) with StringIO("w") as tex: tex.write("\\documentclass{article}\n") tex.write("\\begin{document}\n") tex.write("\\begin{table}\n") - tex.write("\\caption{Parameters for PSR %s}\n" % psrname) + tex.write("\\caption{Parameters for PSR %s}\n" % model.PSR.value) tex.write("\\begin{tabular}{ll}\n") tex.write("\\hline\\hline\n") - tex.write("\\multicolumn{2}{c}{Dataset and Fit summary}\\\\ \n") + tex.write("\\multicolumn{2}{c}{Dataset and model summary}\\\\ \n") + tex.write("\\hline\n") + tex.write( + f"Pulsar name \\dotfill & {model.PSR.value} \\\\ \n" + ) + tex.write( + f"MJD range \\dotfill & {mjd_start}---{mjd_end} \\\\ \n" + ) + tex.write( + f"Data span (yr) \\dotfill & {data_span_yr:.2f} \\\\ \n" + ) + tex.write(f"Number of TOAs \\dotfill & {len(toas)} \\\\ \n") + tex.write( + f"TOA paradigm \\dotfill & {'Wideband' if toas.is_wideband() else 'Narrowband'} \\\\ \n" + ) + tex.write( + f"Solar system ephemeris \\dotfill & {model.EPHEM.value} \\\\ \n" + ) + tex.write( + f"Timescale \\dotfill & {model.CLOCK.value} \\\\ \n" + ) + tex.write( + f"Time unit \\dotfill & {model.UNITS.value} \\\\ \n" + ) + tex.write( + f"Time ephemeris \\dotfill & {model.TIMEEPH.value} \\\\ \n" + ) + + if model.BINARY.value is not None: + tex.write( + f"Binary model \\dotfill & {model.BINARY.value} \\\\ \n" + ) + + if "PhaseJump" in model.components: + tex.write( + f"Number of JUMPs \\dotfill & {model.get_number_of_jumps()} \\\\ \n" + ) + + if "DispersionDMX" in model.components: + tex.write( + f"Number of DMX ranges \\dotfill & {len(model.components['DispersionDMX'].get_indices())} \\\\ \n" + ) + + if "SolarWindDispersionX" in model.components: + tex.write( + f"Number of SWX ranges \\dotfill & {len(model.components['SolarWindDispersionX'].get_indices())} \\\\ \n" + ) + + if "Glitch" in model.components: + tex.write( + f"Number of Glitches \\dotfill & {len(model.components['Glitch'].glitch_indices)} \\\\ \n" + ) + + if "FD" in model.components: + tex.write( + f"Number of FD parameters \\dotfill & {model.num_FD_terms} \\\\ \n" + ) + + if "ScaleToaError" in model.components: + tex.write( + f"Number of EFACs \\dotfill & {len(model.EFACs)} \\\\ \n" + ) + tex.write( + f"Number of EQUADs \\dotfill & {len(model.EQUADs)} \\\\ \n" + ) + + if "EcorrNoise" in model.components: + tex.write( + f"Number of ECORRs \\dotfill & {len(model.ECORRs)} \\\\ \n" + ) + + if "ScaleDmError" in model.components: + tex.write( + f"Number of DMEFACs \\dotfill & {len(model.DMEFACs)} \\\\ \n" + ) + tex.write( + f"Number of DMEQUADs \\dotfill & {len(model.DMEQUADs)} \\\\ \n" + ) + + tex.write("\\hline\n") + + tex.write("\\multicolumn{2}{c}{Fit summary}\\\\ \n") tex.write("\\hline\n") - tex.write("Pulsar name \\dotfill & %s \\\\ \n" % psrname) tex.write( - "MJD range \\dotfill & %d---%d \\\\ \n" - % (mjd_start, mjd_end) + f"Number of free parameters \\dotfill & {len(model.free_params)} \\\\ \n" ) + tex.write(f"Fitting method \\dotfill & {fit_method} \\\\ \n") tex.write( - "Data span (yr) \\dotfill & %.2f \\\\ \n" % data_span_yr + f"RMS TOA residuals ($\\mu s$) \\dotfill & {res.calc_time_resids().to('us').value.std():.2f} \\\\ \n" ) - tex.write("Number of TOAs \\dotfill & %d \\\\ \n" % ntoas) - tex.write("Number of free parameters \\dotfill & %d \\\\ \n" % nfree) - tex.write("TOA paradigm \\dotfill & %s \\\\ \n" % toa_type) + tex.write(f"chi2 \\dotfill & {res.chi2:.2f} \\\\ \n") tex.write( - "Fitting method \\dotfill & %s \\\\ \n" % fit_method + f"Reduced chi2 \\dotfill & {res.chi2_reduced:.2f} \\\\ \n" ) - tex.write("Solar system ephemeris \\dotfill & %s \\\\ \n" % ephem) - tex.write("Timescale \\dotfill & %s \\\\ \n" % clock) - tex.write("Time unit \\dotfill & %s \\\\ \n" % units) - tex.write("Time ephemeris \\dotfill & %s \\\\ \n" % timeeph) - tex.write("RMS TOA residuals ($\\mu s$) \\dotfill & %.2f \\\\ \n" % rms_res) - tex.write("chi2 \\dotfill & %.2f \\\\ \n" % chi2) - tex.write("Reduced chi2 \\dotfill & %.2f \\\\ \n" % chi2_red) tex.write("\\hline\n") tex.write("\multicolumn{2}{c}{Measured Quantities} \\\\ \n") for fp in model.free_params: - if fp not in exclude_params and all( - [not fp.startswith(pre) for pre in exclude_prefixes] + param = getattr(model, fp) + if ( + fp not in exclude_params + and all( + [not isinstance(param._parent, exc) for exc in exclude_components] + ) + and (param.value != 0 or include_zeros) ): - param = getattr(model, fp) - - if isinstance(param._parent, NoiseComponent) and not include_noise: - continue - - if param.value == 0 and not include_zeros: - continue - - if isinstance(param, MJDParameter): - uf = ufloat(param.value, param.uncertainty_value) - tex.write( - "%s, %s (%s)\dotfill & %s \\\\ \n" - % ( - param.name, - param.description, - str(param.units), - f"{uf:.1uS}", - ) - ) - elif isinstance(param, maskParameter): - tex.write( - "%s %s %s, %s (%s)\dotfill & %s \\\\ \n" - % ( - param.prefix, - param.key, - " ".join(param.key_value), - param.description, - str(param.units), - f"{param.as_ufloat():.1uS}", - ) - ) - else: - tex.write( - "%s, %s (%s)\dotfill & %s \\\\ \n" - % ( - param.name, - param.description, - str(param.units), - f"{param.as_ufloat():.1uS}", - ) - ) + tex.write(publish_param(param)) + tex.write("\\hline\n") tex.write("\multicolumn{2}{c}{Set Quantities} \\\\ \n") @@ -158,82 +224,17 @@ def publish( for p in model.params: param = getattr(model, p) - if isinstance(param._parent, NoiseComponent) and not include_noise: - continue - - if param.value == 0 and not include_zeros: - continue - if ( param.value is not None and param.frozen and p not in exclude_params - and all([not p.startswith(pre) for pre in exclude_prefixes]) + and all( + [not isinstance(param._parent, exc) for exc in exclude_components] + ) + and (param.value != 0 or include_zeros) ): - if isinstance(param, maskParameter): - tex.write( - "%s %s %s, %s (%s)\dotfill & %f \\\\ \n" - % ( - param.prefix, - param.key, - " ".join(param.key_value), - param.description, - str(param.units), - param.value, - ) - ) - elif isinstance( - param, - (floatParameter, AngleParameter, MJDParameter, prefixParameter), - ): - tex.write( - "%s, %s (%s)\dotfill & %f \\\\ \n" - % (param.name, param.description, str(param.units), param.value) - ) - elif isinstance(param, MJDParameter): - tex.write( - "%s, %s (%s)\dotfill & %f \\\\ \n" - % (param.name, param.description, str(param.units), param.value) - ) - elif isinstance(param, strParameter): - tex.write( - "%s, %s \dotfill & %s \\\\ \n" - % (param.name, param.description, param.value) - ) - elif isinstance(param, boolParameter): - tex.write( - "%s, %s (Y/N)\dotfill & %s \\\\ \n" - % (param.name, param.description, "Y" if param.value else "N") - ) - elif isinstance(param, intParameter): - tex.write( - "%s, %s \dotfill & %d \\\\ \n" - % (param.name, param.description, param.value) - ) - - # Epoch of frequency determination (MJD)\dotfill & 53750 \\ - # Epoch of position determination (MJD)\dotfill & 53750 \\ - # Epoch of dispersion measure determination (MJD)\dotfill & 53750 \\ - # NE_SW (cm^-3)\dotfill & 0 \\ - # \hline - # \multicolumn{2}{c}{Derived Quantities} \\ - # \hline - # $\log_{10}$(Characteristic age, yr) \dotfill & 8.92 \\ - # $\log_{10}$(Surface magnetic field strength, G) \dotfill & 9.36 \\ - # $\log_{10}$(Edot, ergs/s) \dotfill & 33.46 \\ - # \hline - # \multicolumn{2}{c}{Assumptions} \\ - # \hline - # Clock correction procedure\dotfill & TT(BIPM2019) \\ - # Solar system ephemeris model\dotfill & DE421 \\ - # Binary model\dotfill & NONE \\ - # TDB units (tempo1 mode)\dotfill & Y \\ - # FB90 time ephemeris (tempo1 mode)\dotfill & Y \\ - # Shapiro delay due to planets\dotfill & N \\ - # Tropospheric delay\dotfill & N \\ - # Dilate frequency\dotfill & N \\ - # Electron density at 1 AU (cm$^{-3}$)\dotfill & 0.00 \\ - # Model version number\dotfill & 2.00 \\ + tex.write(publish_param(param)) + tex.write("\\hline\n") tex.write("\\end{tabular}\n") tex.write("\\end{table}\n") From 8af98a42fdb7d7b8af371bb8910dce9881af991b Mon Sep 17 00:00:00 2001 From: Abhimanyu Susobhanan Date: Mon, 21 Aug 2023 14:12:22 -0500 Subject: [PATCH 07/25] fix param namess --- src/pint/models/binary_ell1.py | 4 ++-- src/pint/models/pulsar_binary.py | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/pint/models/binary_ell1.py b/src/pint/models/binary_ell1.py index c0051a446..5d8d6697c 100644 --- a/src/pint/models/binary_ell1.py +++ b/src/pint/models/binary_ell1.py @@ -117,7 +117,7 @@ def __init__(self): floatParameter( name="EPS1", units="", - description="First Laplace-Lagrange parameter, ECC x sin(OM) for ELL1 model", + description="First Laplace-Lagrange parameter", long_double=True, ) ) @@ -126,7 +126,7 @@ def __init__(self): floatParameter( name="EPS2", units="", - description="Second Laplace-Lagrange parameter, ECC x cos(OM) for ELL1 model", + description="Second Laplace-Lagrange parameter", long_double=True, ) ) diff --git a/src/pint/models/pulsar_binary.py b/src/pint/models/pulsar_binary.py index c5089ab7d..98278784e 100644 --- a/src/pint/models/pulsar_binary.py +++ b/src/pint/models/pulsar_binary.py @@ -102,7 +102,9 @@ def __init__(self): ) self.add_param( floatParameter( - name="A1", units=ls, description="Projected semi-major axis, a*sin(i)" + name="A1", + units=ls, + description="Projected semi-major axis of pulsar orbit", ) ) # NOTE: the DOT here takes the value and times 1e-12, tempo/tempo2 can From 52bdcae5950eb04f4e186b0d5a40066db2dc423d Mon Sep 17 00:00:00 2001 From: Abhimanyu Susobhanan Date: Mon, 21 Aug 2023 14:12:41 -0500 Subject: [PATCH 08/25] wideband --- src/pint/output/publish.py | 50 ++++++++++++++++++++++++++++++++------ 1 file changed, 43 insertions(+), 7 deletions(-) diff --git a/src/pint/output/publish.py b/src/pint/output/publish.py index bab7c960f..45ccbb785 100644 --- a/src/pint/output/publish.py +++ b/src/pint/output/publish.py @@ -5,18 +5,20 @@ Glitch, PhaseJump, SolarWindDispersionX, + AbsPhase, ) -from pint.models.absolute_phase import AbsPhase +from pint.models.dispersion_model import DispersionJump from pint.models.noise_model import NoiseComponent from pint.models.parameter import ( Parameter, boolParameter, + funcParameter, intParameter, maskParameter, strParameter, ) from pint.toa import TOAs -from pint.residuals import Residuals +from pint.residuals import Residuals, WidebandTOAResiduals from io import StringIO import numpy as np @@ -65,7 +67,13 @@ def publish( else "WLS" ) - res = Residuals(toas, model) + if toas.is_wideband(): + res = WidebandTOAResiduals(toas, model) + toares = res.toa + dmres = res.dm + else: + res = Residuals(toas, model) + toares = res exclude_params = [ "START", @@ -89,7 +97,7 @@ def publish( if not include_dmx: exclude_components.append(DispersionDMX) if not include_jumps: - exclude_components.append(PhaseJump) + exclude_components.extend([PhaseJump, DispersionJump]) if not include_fd: exclude_components.append(FD) if not include_noise: @@ -147,6 +155,11 @@ def publish( f"Number of JUMPs \\dotfill & {model.get_number_of_jumps()} \\\\ \n" ) + if "DispersionJump" in model.components: + tex.write( + f"Number of DMJUMPs \\dotfill & {len(model.dm_jumps)} \\\\ \n" + ) + if "DispersionDMX" in model.components: tex.write( f"Number of DMX ranges \\dotfill & {len(model.components['DispersionDMX'].get_indices())} \\\\ \n" @@ -197,15 +210,25 @@ def publish( ) tex.write(f"Fitting method \\dotfill & {fit_method} \\\\ \n") tex.write( - f"RMS TOA residuals ($\\mu s$) \\dotfill & {res.calc_time_resids().to('us').value.std():.2f} \\\\ \n" + f"RMS TOA residuals ($\\mu s$) \\dotfill & {toares.calc_time_resids().to('us').value.std():.2e} \\\\ \n" ) - tex.write(f"chi2 \\dotfill & {res.chi2:.2f} \\\\ \n") + if toas.is_wideband(): + tex.write( + f"RMS DM residuals (pc / cm3) \\dotfill & {dmres.calc_resids().to('pc/cm^3').value.std():.2e} \\\\ \n" + ) tex.write( - f"Reduced chi2 \\dotfill & {res.chi2_reduced:.2f} \\\\ \n" + f"$\\chi^2$ \\dotfill & {res.chi2:.2f} \\\\ \n" ) + if toas.is_wideband(): + tex.write(f"Degrees of freedom \\dotfill & {res.dof} \\\\ \n") + else: + tex.write( + f"Reduced $\\chi^2$ \\dotfill & {res.chi2_reduced:.2f} \\\\ \n" + ) tex.write("\\hline\n") tex.write("\multicolumn{2}{c}{Measured Quantities} \\\\ \n") + tex.write("\\hline\n") for fp in model.free_params: param = getattr(model, fp) if ( @@ -232,14 +255,27 @@ def publish( [not isinstance(param._parent, exc) for exc in exclude_components] ) and (param.value != 0 or include_zeros) + and not isinstance(param, funcParameter) ): tex.write(publish_param(param)) tex.write("\\hline\n") + + tex.write("\multicolumn{2}{c}{Derived Quantities} \\\\ \n") + tex.write("\\hline\n") + for p in model.params: + param = getattr(model, p) + + if param.value is not None and isinstance(param, funcParameter): + tex.write(publish_param(param)) + tex.write("\\hline\n") + tex.write("\\end{tabular}\n") tex.write("\\end{table}\n") tex.write("\\end{document}\n") output = tex.getvalue() + output = output.replace("_", "\\_") + return output From a23008a0e963a1c4771a0e87629f8ae632ab4ef2 Mon Sep 17 00:00:00 2001 From: Abhimanyu Susobhanan Date: Mon, 21 Aug 2023 14:48:58 -0500 Subject: [PATCH 09/25] script --- setup.cfg | 1 + src/pint/scripts/pintempo.py | 2 +- src/pint/scripts/pintpublish.py | 79 +++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 src/pint/scripts/pintpublish.py diff --git a/setup.cfg b/setup.cfg index 80a0c6408..52fa9ec59 100644 --- a/setup.cfg +++ b/setup.cfg @@ -61,6 +61,7 @@ console_scripts = convert_parfile = pint.scripts.convert_parfile:main compare_parfiles = pint.scripts.compare_parfiles:main tcb2tdb = pint.scripts.tcb2tdb:main + pintpublish = pint.scripts.pintpublish:main # See the docstring in versioneer.py for instructions. Note that you must diff --git a/src/pint/scripts/pintempo.py b/src/pint/scripts/pintempo.py index 7d21c6323..d99c772ba 100755 --- a/src/pint/scripts/pintempo.py +++ b/src/pint/scripts/pintempo.py @@ -27,7 +27,7 @@ def main(argv=None): parser = argparse.ArgumentParser( - description="Command line interfact to PINT", + description="Command line interface to PINT", formatter_class=argparse.ArgumentDefaultsHelpFormatter, ) parser.add_argument("parfile", help="par file to read model from") diff --git a/src/pint/scripts/pintpublish.py b/src/pint/scripts/pintpublish.py new file mode 100644 index 000000000..e4952ce94 --- /dev/null +++ b/src/pint/scripts/pintpublish.py @@ -0,0 +1,79 @@ +from pint.models import get_model_and_toas +from pint.output.publish import publish +import argparse + + +def main(argv=None): + parser = argparse.ArgumentParser( + description="Publication output for PINT", + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + ) + + parser.add_argument("parfile", help="par file to read model from") + parser.add_argument("timfile", help="TOA file name") + parser.add_argument("--outfile", help="Output file", default=None) + parser.add_argument( + "--include_dmx", + help="Include DMX parameters", + action="store_true", + default=False, + ) + parser.add_argument( + "--include_noise", + help="Include noise parameters", + action="store_true", + default=False, + ) + parser.add_argument( + "--include_jumps", help="Include jumps", action="store_true", default=False + ) + parser.add_argument( + "--include_zeros", + help="Include parameters equal to 0", + action="store_true", + default=False, + ) + parser.add_argument( + "--include_fd", help="Include FD parameters", action="store_true", default=False + ) + parser.add_argument( + "--include_glitches", + help="Include glitches", + action="store_true", + default=False, + ) + parser.add_argument( + "--include_swx", + help="Include SWX parameters", + action="store_true", + default=False, + ) + parser.add_argument( + "--include_tzr", + help="Include TZR parameters", + action="store_true", + default=False, + ) + + args = parser.parse_args(argv) + + model, toas = get_model_and_toas(args.parfile, args.timfile) + + output = publish( + model, + toas, + include_dmx=args.include_dmx, + include_noise=args.include_noise, + include_jumps=args.include_jumps, + include_zeros=args.include_zeros, + include_fd=args.include_fd, + include_glitches=args.include_glitches, + include_swx=args.include_swx, + include_tzr=args.include_tzr, + ) + + if args.outfile is None: + print(output) + else: + with open(args.outfile, "w") as f: + f.write(output) From 5e2caddb3d134e247a821f2e53bf0ffd453ac851 Mon Sep 17 00:00:00 2001 From: Abhimanyu Susobhanan Date: Mon, 21 Aug 2023 14:49:16 -0500 Subject: [PATCH 10/25] wave --- src/pint/output/publish.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/pint/output/publish.py b/src/pint/output/publish.py index 45ccbb785..c2eab900c 100644 --- a/src/pint/output/publish.py +++ b/src/pint/output/publish.py @@ -6,6 +6,7 @@ PhaseJump, SolarWindDispersionX, AbsPhase, + Wave, ) from pint.models.dispersion_model import DispersionJump from pint.models.noise_model import NoiseComponent @@ -15,6 +16,7 @@ funcParameter, intParameter, maskParameter, + pairParameter, strParameter, ) from pint.toa import TOAs @@ -93,7 +95,7 @@ def publish( "BINARY", ] - exclude_components = [] + exclude_components = [Wave] if not include_dmx: exclude_components.append(DispersionDMX) if not include_jumps: @@ -200,6 +202,10 @@ def publish( tex.write( f"Number of DMEQUADs \\dotfill & {len(model.DMEQUADs)} \\\\ \n" ) + if "Wave" in model.components: + tex.write( + f"Number of WAVE components \\dotfill & {model.num_wave_terms} \\\\ \n" + ) tex.write("\\hline\n") @@ -232,10 +238,8 @@ def publish( for fp in model.free_params: param = getattr(model, fp) if ( - fp not in exclude_params - and all( - [not isinstance(param._parent, exc) for exc in exclude_components] - ) + all([not isinstance(param._parent, exc) for exc in exclude_components]) + and fp not in exclude_params and (param.value != 0 or include_zeros) ): tex.write(publish_param(param)) @@ -248,12 +252,10 @@ def publish( param = getattr(model, p) if ( - param.value is not None + all([not isinstance(param._parent, exc) for exc in exclude_components]) + and param.value is not None and param.frozen and p not in exclude_params - and all( - [not isinstance(param._parent, exc) for exc in exclude_components] - ) and (param.value != 0 or include_zeros) and not isinstance(param, funcParameter) ): @@ -266,7 +268,7 @@ def publish( for p in model.params: param = getattr(model, p) - if param.value is not None and isinstance(param, funcParameter): + if isinstance(param, funcParameter) and param.value is not None: tex.write(publish_param(param)) tex.write("\\hline\n") From 49466e3e717d57aa277772b6eff1f753482c9e0c Mon Sep 17 00:00:00 2001 From: Abhimanyu Susobhanan Date: Mon, 21 Aug 2023 14:51:10 -0500 Subject: [PATCH 11/25] log --- src/pint/output/publish.py | 1 - src/pint/scripts/pintpublish.py | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pint/output/publish.py b/src/pint/output/publish.py index c2eab900c..cf5fa7c1a 100644 --- a/src/pint/output/publish.py +++ b/src/pint/output/publish.py @@ -16,7 +16,6 @@ funcParameter, intParameter, maskParameter, - pairParameter, strParameter, ) from pint.toa import TOAs diff --git a/src/pint/scripts/pintpublish.py b/src/pint/scripts/pintpublish.py index e4952ce94..150ff2b9a 100644 --- a/src/pint/scripts/pintpublish.py +++ b/src/pint/scripts/pintpublish.py @@ -1,9 +1,12 @@ from pint.models import get_model_and_toas from pint.output.publish import publish +from pint.logging import setup as setup_log import argparse def main(argv=None): + setup_log(level="WARNING") + parser = argparse.ArgumentParser( description="Publication output for PINT", formatter_class=argparse.ArgumentDefaultsHelpFormatter, From 8d06f356d5f2c9a597e77d36e1368a749dcb02a7 Mon Sep 17 00:00:00 2001 From: Abhimanyu Susobhanan Date: Mon, 21 Aug 2023 15:01:31 -0500 Subject: [PATCH 12/25] docs --- src/pint/output/publish.py | 34 +++++++++++++++++++++++++++++++++ src/pint/scripts/pintpublish.py | 1 + 2 files changed, 35 insertions(+) diff --git a/src/pint/output/publish.py b/src/pint/output/publish.py index cf5fa7c1a..59011840b 100644 --- a/src/pint/output/publish.py +++ b/src/pint/output/publish.py @@ -1,3 +1,4 @@ +"""Generate LaTeX summary of a timing model and TOAs.""" from pint.models import ( TimingModel, DispersionDMX, @@ -25,6 +26,7 @@ def publish_param_value(param: Parameter): + """Return LaTeX string for a parameter value""" if isinstance(param, boolParameter): return "Y" if param.value else "N" elif isinstance(param, strParameter): @@ -36,10 +38,12 @@ def publish_param_value(param: Parameter): def publish_param_unit(param: Parameter): + """Return LaTeX string for a parameter unit""" return "" if param.units == "" or param.units is None else f" ({param.units})" def publish_param(param): + """Return LaTeX line for a parameter""" if isinstance(param, maskParameter): return f"{param.prefix} {param.key} {' '.join(param.key_value)}, {param.description}{publish_param_unit(param)}\dotfill & {publish_param_value(param)} \\\\ \n" else: @@ -58,6 +62,36 @@ def publish( include_swx=False, include_tzr=False, ): + """Generate LaTeX summary of a given timing model and TOAs. + + Parameters + ---------- + model: pint.model.timing_model.TimingModel + Input timing model + toas: TOAs + Input TOAs + include_dmx: bool + Whether to include DMX paremeters (default is False) + include_noise: bool + Whether to include noise paremeters (default is False) + include_jumps: bool + Whether to include jump paremeters (JUMPs, DMJUMPs) (default is False) + include_zeros: bool + Whether to include paremeters which are zero (default is False) + include_fd: bool + Whether to include FD paremeters (default is False) + include_glitches: bool + Whether to include glitch paremeters (default is False) + include_swx: bool + Whether to include SWX paremeters (default is False) + include_tzr: bool + Whether to include TZR paremeters (default is False) + + Returns + ------- + latex_summary: str + The LaTeX summary + """ mjds = toas.get_mjds() mjd_start, mjd_end = int(min(mjds.value)), int(max(mjds.value)) data_span_yr = (mjd_end - mjd_start) / 365.25 diff --git a/src/pint/scripts/pintpublish.py b/src/pint/scripts/pintpublish.py index 150ff2b9a..69b551dc0 100644 --- a/src/pint/scripts/pintpublish.py +++ b/src/pint/scripts/pintpublish.py @@ -1,3 +1,4 @@ +"""Generate LaTeX summary of a timing model and TOAs.""" from pint.models import get_model_and_toas from pint.output.publish import publish from pint.logging import setup as setup_log From 22de9e23aa088ef2838af8a581b6e06958b30a5d Mon Sep 17 00:00:00 2001 From: Abhimanyu Susobhanan Date: Mon, 21 Aug 2023 17:16:50 -0500 Subject: [PATCH 13/25] test --- src/pint/output/publish.py | 2 +- tests/test_publish.py | 72 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 tests/test_publish.py diff --git a/src/pint/output/publish.py b/src/pint/output/publish.py index 59011840b..11cc87689 100644 --- a/src/pint/output/publish.py +++ b/src/pint/output/publish.py @@ -262,7 +262,7 @@ def publish( tex.write(f"Degrees of freedom \\dotfill & {res.dof} \\\\ \n") else: tex.write( - f"Reduced $\\chi^2$ \\dotfill & {res.chi2_reduced:.2f} \\\\ \n" + f"Reduced $\\chi^2$ \\dotfill & {res.reduced_chi2:.2f} \\\\ \n" ) tex.write("\\hline\n") diff --git a/tests/test_publish.py b/tests/test_publish.py new file mode 100644 index 000000000..4e1b23cca --- /dev/null +++ b/tests/test_publish.py @@ -0,0 +1,72 @@ +from pinttestdata import datadir +import pytest +from pint.models import get_model_and_toas +from pint.output.publish import publish +from pint.scripts import pintpublish +import os + +data_NGC6440E = get_model_and_toas(datadir / "NGC6440E.par", datadir / "NGC6440E.tim") + + +def test_NGC6440E(): + m, t = data_NGC6440E + output = publish(m, t) + assert "NGC6440E" in output + assert "DE421" in output + + +data_J0613m0200_NANOGrav_9yv1 = get_model_and_toas( + datadir / "J0613-0200_NANOGrav_9yv1.gls.par", + datadir / "J0613-0200_NANOGrav_9yv1.tim", +) + + +@pytest.mark.parametrize("full", [True, False]) +def test_J0613m0200_NANOGrav_9yv1(full): + m, t = data_J0613m0200_NANOGrav_9yv1 + output = publish( + m, t, include_dmx=full, include_fd=full, include_noise=full, include_jumps=full + ) + + assert "J0613-0200" in output + assert "ELL1" in output + assert "Narrowband" in output + assert "freedom" not in output + assert "Reduced" in output + assert not full or "DMX" in output + assert not full or "JUMP" in output + assert not full or "FD1" in output + assert not full or "RNAMP" in output + + +data_J1614m2230_NANOGrav_12yv3_wb = get_model_and_toas( + datadir / "J1614-2230_NANOGrav_12yv3.wb.gls.par", + datadir / "J1614-2230_NANOGrav_12yv3.wb.tim", +) + + +@pytest.mark.parametrize("full", [True, False]) +def test_J1614m2230_NANOGrav_12yv3_wb(full): + m, t = data_J1614m2230_NANOGrav_12yv3_wb + output = publish( + m, t, include_dmx=full, include_fd=full, include_noise=full, include_jumps=full + ) + + assert "DE436" in output + assert "ELL1" in output + assert "Wideband" in output + assert "freedom" in output + assert not full or "DMX" in output + assert not full or "DMJUMP" in output + assert not full or "JUMP" in output + assert "TT(BIPM2017)" in output + + +@pytest.mark.parametrize("file", [True, False]) +def test_script(file): + par, tim = str(datadir / "NGC6440E.par"), str(datadir / "NGC6440E.tim") + outfile = "--outfile=pintpublish_test.tex" if file else "" + args = f"{par} {tim} {outfile}" + pintpublish.main(args.split()) + + assert not file or os.path.isfile("pintpublish_test.tex") From 9f1e4c1274f9e333a0008ddb4a28a0ce0a349ef1 Mon Sep 17 00:00:00 2001 From: Abhimanyu Susobhanan Date: Mon, 21 Aug 2023 17:23:22 -0500 Subject: [PATCH 14/25] changelog --- CHANGELOG-unreleased.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG-unreleased.md b/CHANGELOG-unreleased.md index 2a67299aa..ad4c097d4 100644 --- a/CHANGELOG-unreleased.md +++ b/CHANGELOG-unreleased.md @@ -19,6 +19,7 @@ the released changes. - `pint.print_info()` function for bug reporting - Added an autocorrelation function to check for chain convergence in `event_optimize` - Minor doc updates to explain default NHARMS and missing derivative functions +- `pint.output.publish` module and `pintpublish` script for generating publication (LaTeX) output. ### Fixed - Deleting JUMP1 from flag tables will not prevent fitting - Simulating TOAs from tim file when PLANET_SHAPIRO is true now works From 466957304fc184ad7027683f809bec7aa0dea80c Mon Sep 17 00:00:00 2001 From: Abhimanyu Susobhanan Date: Tue, 22 Aug 2023 08:10:16 -0500 Subject: [PATCH 15/25] fix test --- tests/test_publish.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_publish.py b/tests/test_publish.py index 4e1b23cca..f8663ff3d 100644 --- a/tests/test_publish.py +++ b/tests/test_publish.py @@ -11,7 +11,7 @@ def test_NGC6440E(): m, t = data_NGC6440E output = publish(m, t) - assert "NGC6440E" in output + assert "1748-2021E" in output assert "DE421" in output From 7876c30347af1e3d5f960ab4c7ab96c612f214e9 Mon Sep 17 00:00:00 2001 From: Abhimanyu Susobhanan Date: Tue, 22 Aug 2023 12:58:28 -0500 Subject: [PATCH 16/25] latex formatting --- src/pint/output/publish.py | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/pint/output/publish.py b/src/pint/output/publish.py index 11cc87689..495928913 100644 --- a/src/pint/output/publish.py +++ b/src/pint/output/publish.py @@ -34,12 +34,14 @@ def publish_param_value(param: Parameter): elif isinstance(param, intParameter): return str(param.value) else: - return f"{param.as_ufloat():.1uS}" if not param.frozen else f"{param.value:f}" + return ( + f"${param.as_ufloat():.1uSL}$" if not param.frozen else f"{param.value:f}" + ) def publish_param_unit(param: Parameter): """Return LaTeX string for a parameter unit""" - return "" if param.units == "" or param.units is None else f" ({param.units})" + return "" if param.units == "" or param.units is None else f" ({param.units:latex})" def publish_param(param): @@ -155,7 +157,7 @@ def publish( tex.write("\\multicolumn{2}{c}{Dataset and model summary}\\\\ \n") tex.write("\\hline\n") tex.write( - f"Pulsar name \\dotfill & {model.PSR.value} \\\\ \n" + f"Pulsar name \\dotfill & {model.PSR.value.replace('-','$-$')} \\\\ \n" ) tex.write( f"MJD range \\dotfill & {mjd_start}---{mjd_end} \\\\ \n" @@ -249,11 +251,11 @@ def publish( ) tex.write(f"Fitting method \\dotfill & {fit_method} \\\\ \n") tex.write( - f"RMS TOA residuals ($\\mu s$) \\dotfill & {toares.calc_time_resids().to('us').value.std():.2e} \\\\ \n" + f"RMS TOA residuals ($\\mu s$) \\dotfill & {toares.calc_time_resids().to('us').value.std():.2f} \\\\ \n" ) if toas.is_wideband(): tex.write( - f"RMS DM residuals (pc / cm3) \\dotfill & {dmres.calc_resids().to('pc/cm^3').value.std():.2e} \\\\ \n" + f"RMS DM residuals (pc / cm3) \\dotfill & {dmres.calc_resids().to('pc/cm^3').value.std():.2f} \\\\ \n" ) tex.write( f"$\\chi^2$ \\dotfill & {res.chi2:.2f} \\\\ \n" @@ -296,14 +298,15 @@ def publish( tex.write("\\hline\n") - tex.write("\multicolumn{2}{c}{Derived Quantities} \\\\ \n") - tex.write("\\hline\n") - for p in model.params: - param = getattr(model, p) - - if isinstance(param, funcParameter) and param.value is not None: + derived_params = [ + p for p in model.params if isinstance(getattr(model, p), funcParameter) + ] + if len(derived_params) > 0: + tex.write("\multicolumn{2}{c}{Derived Quantities} \\\\ \n") + tex.write("\\hline\n") + for param in derived_params: tex.write(publish_param(param)) - tex.write("\\hline\n") + tex.write("\\hline\n") tex.write("\\end{tabular}\n") tex.write("\\end{table}\n") From 1296d08b9e3fdb481d8c5d0ccafccb266059b551 Mon Sep 17 00:00:00 2001 From: Abhimanyu Susobhanan Date: Tue, 22 Aug 2023 13:10:59 -0500 Subject: [PATCH 17/25] disable fraction --- src/pint/output/publish.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/pint/output/publish.py b/src/pint/output/publish.py index 495928913..4e99d1006 100644 --- a/src/pint/output/publish.py +++ b/src/pint/output/publish.py @@ -41,7 +41,11 @@ def publish_param_value(param: Parameter): def publish_param_unit(param: Parameter): """Return LaTeX string for a parameter unit""" - return "" if param.units == "" or param.units is None else f" ({param.units:latex})" + return ( + "" + if param.units == "" or param.units is None + else f" ({param.units.to_string(format='latex', fraction=False)})" + ) def publish_param(param): From 1ca2246a3fb7c1795b1260337ecea72770760150 Mon Sep 17 00:00:00 2001 From: Abhimanyu Susobhanan Date: Tue, 22 Aug 2023 13:11:17 -0500 Subject: [PATCH 18/25] desc --- src/pint/models/binary_ell1.py | 4 ++-- src/pint/models/frequency_dependent.py | 2 +- src/pint/models/noise_model.py | 2 +- src/pint/models/pulsar_binary.py | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/pint/models/binary_ell1.py b/src/pint/models/binary_ell1.py index ef0331e2a..8e3c93b4e 100644 --- a/src/pint/models/binary_ell1.py +++ b/src/pint/models/binary_ell1.py @@ -117,7 +117,7 @@ def __init__(self): floatParameter( name="EPS1", units="", - description="First Laplace-Lagrange parameter", + description="First Laplace-Lagrange parameter, ECC*sin(OM)", long_double=True, ) ) @@ -126,7 +126,7 @@ def __init__(self): floatParameter( name="EPS2", units="", - description="Second Laplace-Lagrange parameter", + description="Second Laplace-Lagrange parameter, ECC*cos(OM)", long_double=True, ) ) diff --git a/src/pint/models/frequency_dependent.py b/src/pint/models/frequency_dependent.py index 7e295a615..4426b69a8 100644 --- a/src/pint/models/frequency_dependent.py +++ b/src/pint/models/frequency_dependent.py @@ -36,7 +36,7 @@ def __init__(self): name="FD1", units="second", value=0.0, - description="Coefficient of frequency-dependent delay", + description="Polynomial coefficient of log-frequency-dependent delay", # descriptionTplt=lambda x: ( # "%d term of frequency" " dependent coefficients" % x # ), diff --git a/src/pint/models/noise_model.py b/src/pint/models/noise_model.py index 455e00230..49036fd8a 100644 --- a/src/pint/models/noise_model.py +++ b/src/pint/models/noise_model.py @@ -301,7 +301,7 @@ def __init__( name="ECORR", units="us", aliases=["TNECORR"], - description="An error term that correlates all TOAs in an observing epoch.", + description="An error term that is correlated among all TOAs in an observing epoch.", ) ) diff --git a/src/pint/models/pulsar_binary.py b/src/pint/models/pulsar_binary.py index 98278784e..b27dc923c 100644 --- a/src/pint/models/pulsar_binary.py +++ b/src/pint/models/pulsar_binary.py @@ -104,7 +104,7 @@ def __init__(self): floatParameter( name="A1", units=ls, - description="Projected semi-major axis of pulsar orbit", + description="Projected semi-major axis of pulsar orbit, ap*sin(i)", ) ) # NOTE: the DOT here takes the value and times 1e-12, tempo/tempo2 can @@ -114,7 +114,7 @@ def __init__(self): name="A1DOT", aliases=["XDOT"], units=ls / u.s, - description="Derivative of projected semi-major axis, da*sin(i)/dt", + description="Derivative of projected semi-major axis, d[ap*sin(i)]/dt", unit_scale=True, scale_factor=1e-12, scale_threshold=1e-7, From 715ad2662c34d688b4f3a424571a6305a973fb93 Mon Sep 17 00:00:00 2001 From: Abhimanyu Susobhanan Date: Tue, 22 Aug 2023 15:20:18 -0500 Subject: [PATCH 19/25] as_latex --- src/pint/models/parameter.py | 32 ++++++++ src/pint/output/publish.py | 152 +++++++++++++++-------------------- 2 files changed, 97 insertions(+), 87 deletions(-) diff --git a/src/pint/models/parameter.py b/src/pint/models/parameter.py index 2b1dd846f..1f7a35609 100644 --- a/src/pint/models/parameter.py +++ b/src/pint/models/parameter.py @@ -571,6 +571,18 @@ def from_parfile_line(self, line): self.uncertainty = self._set_uncertainty(ucty) return True + def value_as_latex(self): + return f"${self.as_ufloat():.1uSL}$" if not self.frozen else f"{self.value:f}" + + def as_latex(self): + unit_latex = ( + "" + if self.units == "" or self.units is None + else f" ({self.units.to_string(format='latex', fraction=False)})" + ) + value_latex = self.value_as_latex() + return f"{self.name}, {self.description}{unit_latex}", value_latex + def add_alias(self, alias): """Add a name to the list of aliases for this parameter.""" self.aliases.append(alias) @@ -865,6 +877,9 @@ def _set_quantity(self, val): """Convert to string.""" return str(val) + def value_as_latex(self): + return self.value + class boolParameter(Parameter): """Boolean-valued parameter. @@ -932,6 +947,9 @@ def _set_quantity(self, val): return bool(float(val)) return bool(val) + def value_as_latex(self): + return "Y" if self.value else "N" + class intParameter(Parameter): """Integer parameter values. @@ -1000,6 +1018,9 @@ def _set_quantity(self, val): return ival + def value_as_latex(self): + return str(self.value) + class MJDParameter(Parameter): """Parameters for MJD quantities. @@ -1582,6 +1603,9 @@ def name_matches(self, name): def as_parfile_line(self, format="pint"): return self.param_comp.as_parfile_line(format=format) + def as_latex(self): + return self.param_comp.as_latex() + def help_line(self): return self.param_comp.help_line() @@ -1927,6 +1951,14 @@ def as_parfile_line(self, format="pint"): line += " 1" return line + "\n" + def as_latex(self): + unit_latex = ( + "" + if self.units == "" or self.units is None + else f" ({self.units.to_string(format='latex', fraction=False)})" + ) + return f"{self.prefix} {self.key} {' '.join(self.key_value)}, {self.description}{unit_latex}\dotfill & {self.value_as_latex()} \\\\ \n" + def new_param(self, index, copy_all=False): """Create a new but same style mask parameter""" return ( diff --git a/src/pint/output/publish.py b/src/pint/output/publish.py index 4e99d1006..e17fdb52e 100644 --- a/src/pint/output/publish.py +++ b/src/pint/output/publish.py @@ -13,11 +13,7 @@ from pint.models.noise_model import NoiseComponent from pint.models.parameter import ( Parameter, - boolParameter, funcParameter, - intParameter, - maskParameter, - strParameter, ) from pint.toa import TOAs from pint.residuals import Residuals, WidebandTOAResiduals @@ -25,35 +21,10 @@ import numpy as np -def publish_param_value(param: Parameter): - """Return LaTeX string for a parameter value""" - if isinstance(param, boolParameter): - return "Y" if param.value else "N" - elif isinstance(param, strParameter): - return param.value - elif isinstance(param, intParameter): - return str(param.value) - else: - return ( - f"${param.as_ufloat():.1uSL}$" if not param.frozen else f"{param.value:f}" - ) - - -def publish_param_unit(param: Parameter): - """Return LaTeX string for a parameter unit""" - return ( - "" - if param.units == "" or param.units is None - else f" ({param.units.to_string(format='latex', fraction=False)})" - ) - - -def publish_param(param): +def publish_param(param: Parameter): """Return LaTeX line for a parameter""" - if isinstance(param, maskParameter): - return f"{param.prefix} {param.key} {' '.join(param.key_value)}, {param.description}{publish_param_unit(param)}\dotfill & {publish_param_value(param)} \\\\ \n" - else: - return f"{param.name}, {param.description}{publish_param_unit(param)}\dotfill & {publish_param_value(param)} \\\\ \n" + label, value = param.as_latex() + return f"{label}\\dotfill & {value} \\\\ \n" def publish( @@ -67,6 +38,7 @@ def publish( include_glitches=False, include_swx=False, include_tzr=False, + include_prefix_summary=True, ): """Generate LaTeX summary of a given timing model and TOAs. @@ -92,6 +64,8 @@ def publish( Whether to include SWX paremeters (default is False) include_tzr: bool Whether to include TZR paremeters (default is False) + include_prefix_summary: bool + Whether to include a summary of prefix and mask parameters (default is True) Returns ------- @@ -191,60 +165,61 @@ def publish( f"Binary model \\dotfill & {model.BINARY.value} \\\\ \n" ) - if "PhaseJump" in model.components: - tex.write( - f"Number of JUMPs \\dotfill & {model.get_number_of_jumps()} \\\\ \n" - ) - - if "DispersionJump" in model.components: - tex.write( - f"Number of DMJUMPs \\dotfill & {len(model.dm_jumps)} \\\\ \n" - ) - - if "DispersionDMX" in model.components: - tex.write( - f"Number of DMX ranges \\dotfill & {len(model.components['DispersionDMX'].get_indices())} \\\\ \n" - ) - - if "SolarWindDispersionX" in model.components: - tex.write( - f"Number of SWX ranges \\dotfill & {len(model.components['SolarWindDispersionX'].get_indices())} \\\\ \n" - ) - - if "Glitch" in model.components: - tex.write( - f"Number of Glitches \\dotfill & {len(model.components['Glitch'].glitch_indices)} \\\\ \n" - ) - - if "FD" in model.components: - tex.write( - f"Number of FD parameters \\dotfill & {model.num_FD_terms} \\\\ \n" - ) - - if "ScaleToaError" in model.components: - tex.write( - f"Number of EFACs \\dotfill & {len(model.EFACs)} \\\\ \n" - ) - tex.write( - f"Number of EQUADs \\dotfill & {len(model.EQUADs)} \\\\ \n" - ) - - if "EcorrNoise" in model.components: - tex.write( - f"Number of ECORRs \\dotfill & {len(model.ECORRs)} \\\\ \n" - ) - - if "ScaleDmError" in model.components: - tex.write( - f"Number of DMEFACs \\dotfill & {len(model.DMEFACs)} \\\\ \n" - ) - tex.write( - f"Number of DMEQUADs \\dotfill & {len(model.DMEQUADs)} \\\\ \n" - ) - if "Wave" in model.components: - tex.write( - f"Number of WAVE components \\dotfill & {model.num_wave_terms} \\\\ \n" - ) + if include_prefix_summary: + if "PhaseJump" in model.components: + tex.write( + f"Number of JUMPs \\dotfill & {model.get_number_of_jumps()} \\\\ \n" + ) + + if "DispersionJump" in model.components: + tex.write( + f"Number of DMJUMPs \\dotfill & {len(model.dm_jumps)} \\\\ \n" + ) + + if "DispersionDMX" in model.components: + tex.write( + f"Number of DMX ranges \\dotfill & {len(model.components['DispersionDMX'].get_indices())} \\\\ \n" + ) + + if "SolarWindDispersionX" in model.components: + tex.write( + f"Number of SWX ranges \\dotfill & {len(model.components['SolarWindDispersionX'].get_indices())} \\\\ \n" + ) + + if "Glitch" in model.components: + tex.write( + f"Number of Glitches \\dotfill & {len(model.components['Glitch'].glitch_indices)} \\\\ \n" + ) + + if "FD" in model.components: + tex.write( + f"Number of FD parameters \\dotfill & {model.num_FD_terms} \\\\ \n" + ) + + if "ScaleToaError" in model.components: + tex.write( + f"Number of EFACs \\dotfill & {len(model.EFACs)} \\\\ \n" + ) + tex.write( + f"Number of EQUADs \\dotfill & {len(model.EQUADs)} \\\\ \n" + ) + + if "EcorrNoise" in model.components: + tex.write( + f"Number of ECORRs \\dotfill & {len(model.ECORRs)} \\\\ \n" + ) + + if "ScaleDmError" in model.components: + tex.write( + f"Number of DMEFACs \\dotfill & {len(model.DMEFACs)} \\\\ \n" + ) + tex.write( + f"Number of DMEQUADs \\dotfill & {len(model.DMEQUADs)} \\\\ \n" + ) + if "Wave" in model.components: + tex.write( + f"Number of WAVE components \\dotfill & {model.num_wave_terms} \\\\ \n" + ) tex.write("\\hline\n") @@ -303,7 +278,10 @@ def publish( tex.write("\\hline\n") derived_params = [ - p for p in model.params if isinstance(getattr(model, p), funcParameter) + getattr(model, p) + for p in model.params + if isinstance(getattr(model, p), funcParameter) + and getattr(model, p).quantity is not None ] if len(derived_params) > 0: tex.write("\multicolumn{2}{c}{Derived Quantities} \\\\ \n") From 42b0beb44655a741e27c551b849ec096b13fe5e1 Mon Sep 17 00:00:00 2001 From: Abhimanyu Susobhanan Date: Tue, 22 Aug 2023 15:22:10 -0500 Subject: [PATCH 20/25] .gitignore --- .gitignore | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index b76b751df..64a407117 100644 --- a/.gitignore +++ b/.gitignore @@ -119,4 +119,10 @@ docs/examples/*.ipynb docs/examples-rendered/*.py # VSCode wants to put virtualenvs here -.env \ No newline at end of file +.env + +# pintpublish output +*.tex +*.aux +*.log +*.pdf \ No newline at end of file From a17e5adeb9ca91c801c2dbfc2ebc400310ca594b Mon Sep 17 00:00:00 2001 From: Abhimanyu Susobhanan Date: Tue, 22 Aug 2023 15:29:00 -0500 Subject: [PATCH 21/25] options --- src/pint/output/publish.py | 111 ++++++++++++++++++++++--------------- 1 file changed, 65 insertions(+), 46 deletions(-) diff --git a/src/pint/output/publish.py b/src/pint/output/publish.py index e17fdb52e..2cb82b0ac 100644 --- a/src/pint/output/publish.py +++ b/src/pint/output/publish.py @@ -39,6 +39,9 @@ def publish( include_swx=False, include_tzr=False, include_prefix_summary=True, + include_set_params=True, + include_derived_params=True, + include_fit_summary=True, ): """Generate LaTeX summary of a given timing model and TOAs. @@ -66,6 +69,12 @@ def publish( Whether to include TZR paremeters (default is False) include_prefix_summary: bool Whether to include a summary of prefix and mask parameters (default is True) + include_set_params: bool + Whether to include set params (default is True) + include_derived_params: bool + Whether to include derived params (default is True) + include_fit_summary: bool + Whether to include fit summary params (default is True) Returns ------- @@ -223,29 +232,32 @@ def publish( tex.write("\\hline\n") - tex.write("\\multicolumn{2}{c}{Fit summary}\\\\ \n") - tex.write("\\hline\n") - tex.write( - f"Number of free parameters \\dotfill & {len(model.free_params)} \\\\ \n" - ) - tex.write(f"Fitting method \\dotfill & {fit_method} \\\\ \n") - tex.write( - f"RMS TOA residuals ($\\mu s$) \\dotfill & {toares.calc_time_resids().to('us').value.std():.2f} \\\\ \n" - ) - if toas.is_wideband(): + if include_fit_summary: + tex.write("\\multicolumn{2}{c}{Fit summary}\\\\ \n") + tex.write("\\hline\n") tex.write( - f"RMS DM residuals (pc / cm3) \\dotfill & {dmres.calc_resids().to('pc/cm^3').value.std():.2f} \\\\ \n" + f"Number of free parameters \\dotfill & {len(model.free_params)} \\\\ \n" ) - tex.write( - f"$\\chi^2$ \\dotfill & {res.chi2:.2f} \\\\ \n" - ) - if toas.is_wideband(): - tex.write(f"Degrees of freedom \\dotfill & {res.dof} \\\\ \n") - else: tex.write( - f"Reduced $\\chi^2$ \\dotfill & {res.reduced_chi2:.2f} \\\\ \n" + f"Fitting method \\dotfill & {fit_method} \\\\ \n" ) - tex.write("\\hline\n") + tex.write( + f"RMS TOA residuals ($\\mu s$) \\dotfill & {toares.calc_time_resids().to('us').value.std():.2f} \\\\ \n" + ) + if toas.is_wideband(): + tex.write( + f"RMS DM residuals (pc / cm3) \\dotfill & {dmres.calc_resids().to('pc/cm^3').value.std():.2f} \\\\ \n" + ) + tex.write( + f"$\\chi^2$ \\dotfill & {res.chi2:.2f} \\\\ \n" + ) + if toas.is_wideband(): + tex.write(f"Degrees of freedom \\dotfill & {res.dof} \\\\ \n") + else: + tex.write( + f"Reduced $\\chi^2$ \\dotfill & {res.reduced_chi2:.2f} \\\\ \n" + ) + tex.write("\\hline\n") tex.write("\multicolumn{2}{c}{Measured Quantities} \\\\ \n") tex.write("\\hline\n") @@ -260,36 +272,43 @@ def publish( tex.write("\\hline\n") - tex.write("\multicolumn{2}{c}{Set Quantities} \\\\ \n") - tex.write("\\hline\n") - for p in model.params: - param = getattr(model, p) - - if ( - all([not isinstance(param._parent, exc) for exc in exclude_components]) - and param.value is not None - and param.frozen - and p not in exclude_params - and (param.value != 0 or include_zeros) - and not isinstance(param, funcParameter) - ): - tex.write(publish_param(param)) - - tex.write("\\hline\n") - - derived_params = [ - getattr(model, p) - for p in model.params - if isinstance(getattr(model, p), funcParameter) - and getattr(model, p).quantity is not None - ] - if len(derived_params) > 0: - tex.write("\multicolumn{2}{c}{Derived Quantities} \\\\ \n") + if include_set_params: + tex.write("\multicolumn{2}{c}{Set Quantities} \\\\ \n") tex.write("\\hline\n") - for param in derived_params: - tex.write(publish_param(param)) + for p in model.params: + param = getattr(model, p) + + if ( + all( + [ + not isinstance(param._parent, exc) + for exc in exclude_components + ] + ) + and param.value is not None + and param.frozen + and p not in exclude_params + and (param.value != 0 or include_zeros) + and not isinstance(param, funcParameter) + ): + tex.write(publish_param(param)) + tex.write("\\hline\n") + if include_derived_params: + derived_params = [ + getattr(model, p) + for p in model.params + if isinstance(getattr(model, p), funcParameter) + and getattr(model, p).quantity is not None + ] + if len(derived_params) > 0: + tex.write("\multicolumn{2}{c}{Derived Quantities} \\\\ \n") + tex.write("\\hline\n") + for param in derived_params: + tex.write(publish_param(param)) + tex.write("\\hline\n") + tex.write("\\end{tabular}\n") tex.write("\\end{table}\n") tex.write("\\end{document}\n") From 80579c6b9c26d567e176f0eb87cb5750009d9847 Mon Sep 17 00:00:00 2001 From: Abhimanyu Susobhanan Date: Wed, 23 Aug 2023 11:11:38 -0500 Subject: [PATCH 22/25] fix as_latex --- CHANGELOG-unreleased.md | 1 + src/pint/models/parameter.py | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG-unreleased.md b/CHANGELOG-unreleased.md index ad4c097d4..2e1a3f678 100644 --- a/CHANGELOG-unreleased.md +++ b/CHANGELOG-unreleased.md @@ -19,6 +19,7 @@ the released changes. - `pint.print_info()` function for bug reporting - Added an autocorrelation function to check for chain convergence in `event_optimize` - Minor doc updates to explain default NHARMS and missing derivative functions +- `Parameter.as_latex` method for latex representation of a parameter. - `pint.output.publish` module and `pintpublish` script for generating publication (LaTeX) output. ### Fixed - Deleting JUMP1 from flag tables will not prevent fitting diff --git a/src/pint/models/parameter.py b/src/pint/models/parameter.py index 1f7a35609..eb51e0db8 100644 --- a/src/pint/models/parameter.py +++ b/src/pint/models/parameter.py @@ -1957,7 +1957,10 @@ def as_latex(self): if self.units == "" or self.units is None else f" ({self.units.to_string(format='latex', fraction=False)})" ) - return f"{self.prefix} {self.key} {' '.join(self.key_value)}, {self.description}{unit_latex}\dotfill & {self.value_as_latex()} \\\\ \n" + return ( + f"{self.prefix} {self.key} {' '.join(self.key_value)}, {self.description}{unit_latex}", + self.value_as_latex(), + ) def new_param(self, index, copy_all=False): """Create a new but same style mask parameter""" From ab589ff2d773b30f9fa30e46f96372b60e4a47b3 Mon Sep 17 00:00:00 2001 From: Abhimanyu Susobhanan Date: Wed, 23 Aug 2023 12:15:27 -0500 Subject: [PATCH 23/25] deal with old astropy --- src/pint/models/parameter.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/pint/models/parameter.py b/src/pint/models/parameter.py index eb51e0db8..9a3e5b3d9 100644 --- a/src/pint/models/parameter.py +++ b/src/pint/models/parameter.py @@ -1952,11 +1952,19 @@ def as_parfile_line(self, format="pint"): return line + "\n" def as_latex(self): - unit_latex = ( - "" - if self.units == "" or self.units is None - else f" ({self.units.to_string(format='latex', fraction=False)})" - ) + try: + unit_latex = ( + "" + if self.units == "" or self.units is None + else f" ({self.units.to_string(format='latex', fraction=False)})" + ) + except TypeError: + # `fraction` option is not available in old astropy versions. + unit_latex = ( + "" + if self.units == "" or self.units is None + else f" ({self.units.to_string(format='latex')})" + ) return ( f"{self.prefix} {self.key} {' '.join(self.key_value)}, {self.description}{unit_latex}", self.value_as_latex(), From e6a28d9b72689c82221763c23c0741ae7b1190a3 Mon Sep 17 00:00:00 2001 From: Abhimanyu Susobhanan Date: Thu, 24 Aug 2023 09:21:57 -0500 Subject: [PATCH 24/25] old astropy fix --- src/pint/models/parameter.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/pint/models/parameter.py b/src/pint/models/parameter.py index 9a3e5b3d9..5b2c2e36d 100644 --- a/src/pint/models/parameter.py +++ b/src/pint/models/parameter.py @@ -575,11 +575,19 @@ def value_as_latex(self): return f"${self.as_ufloat():.1uSL}$" if not self.frozen else f"{self.value:f}" def as_latex(self): - unit_latex = ( - "" - if self.units == "" or self.units is None - else f" ({self.units.to_string(format='latex', fraction=False)})" - ) + try: + unit_latex = ( + "" + if self.units == "" or self.units is None + else f" ({self.units.to_string(format='latex', fraction=False)})" + ) + except TypeError: + # to deal with old astropy + unit_latex = ( + "" + if self.units == "" or self.units is None + else f" ({self.units.to_string(format='latex')})" + ) value_latex = self.value_as_latex() return f"{self.name}, {self.description}{unit_latex}", value_latex From 6648c4a88666a2cc8f5e00931617e97db56a0d16 Mon Sep 17 00:00:00 2001 From: Abhimanyu Susobhanan Date: Thu, 24 Aug 2023 14:34:42 -0500 Subject: [PATCH 25/25] -- --- .readthedocs.yml | 5 +++-- requirements_dev.txt | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.readthedocs.yml b/.readthedocs.yml index 391de3894..4bfc807fc 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -17,7 +17,8 @@ build: # Build documentation in the docs/ directory with Sphinx sphinx: - configuration: docs/conf.py + builder: html + configuration: docs/conf.py # If using Sphinx, optionally build your docs in additional formats such as PDF formats: @@ -29,4 +30,4 @@ python: - requirements: requirements_dev.txt - requirements: requirements.txt - method: pip - path: . \ No newline at end of file + path: . diff --git a/requirements_dev.txt b/requirements_dev.txt index 54cdeeab1..0f8816b6f 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -3,7 +3,7 @@ pip>=9.0.1 setuptools>=41.0 coverage>=4.3.4 traitlets -Sphinx>=2.2,!=5.1.0 +Sphinx>=2.2,!=5.1.0,<7.0 astropy>=4.0,!=4.0.1,!=4.0.1.post1 #astropy-helpers>=1.3 sphinx-rtd-theme>=1.1.1