diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..6f5f2ad --- /dev/null +++ b/.coveragerc @@ -0,0 +1,3 @@ +[report] +exclude_also = + # pragma: no cover \ No newline at end of file diff --git a/.github/workflows/testing-and-coverage.yml b/.github/workflows/testing-and-coverage.yml index 3e67786..97bb48b 100644 --- a/.github/workflows/testing-and-coverage.yml +++ b/.github/workflows/testing-and-coverage.yml @@ -27,8 +27,8 @@ jobs: run: | sudo apt-get update python -m pip install --upgrade pip - pip install . - pip install .[dev] + pip install -e . + pip install -e .[dev] if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - name: Run unit tests with pytest run: | diff --git a/.gitignore b/.gitignore index a7b6b17..1558b2c 100644 --- a/.gitignore +++ b/.gitignore @@ -164,5 +164,3 @@ cython_debug/ # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ -# ignore large db files -*.db diff --git a/notebooks/adler_examples_local_db.ipynb b/notebooks/adler_examples_local_db.ipynb new file mode 100644 index 0000000..f959401 --- /dev/null +++ b/notebooks/adler_examples_local_db.ipynb @@ -0,0 +1,476 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "d591f5d8-9148-46ff-a62b-0f2a29eb806c", + "metadata": { + "execution": { + "iopub.execute_input": "2024-02-29T16:09:11.533501Z", + "iopub.status.busy": "2024-02-29T16:09:11.533247Z", + "iopub.status.idle": "2024-02-29T16:09:13.062886Z", + "shell.execute_reply": "2024-02-29T16:09:13.061510Z", + "shell.execute_reply.started": "2024-02-29T16:09:11.533477Z" + } + }, + "outputs": [], + "source": [ + "from adler.dataclasses.AdlerPlanetoid import AdlerPlanetoid\n", + "from adler.science.PhaseCurve import PhaseCurve\n", + "\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "import matplotlib.gridspec as gridspec\n", + "import astropy.units as u" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "645efb98-567d-481e-a79c-b1cfdc828726", + "metadata": { + "execution": { + "iopub.execute_input": "2024-02-29T16:09:13.065333Z", + "iopub.status.busy": "2024-02-29T16:09:13.064482Z", + "iopub.status.idle": "2024-02-29T16:09:13.070452Z", + "shell.execute_reply": "2024-02-29T16:09:13.069235Z", + "shell.execute_reply.started": "2024-02-29T16:09:13.065297Z" + } + }, + "outputs": [], + "source": [ + "# ssObjectId of object to analyse\n", + "ssoid = \"8268570668335894776\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10b36aab-b322-49b8-8ff3-49bef68d7416", + "metadata": { + "execution": { + "iopub.execute_input": "2024-02-29T16:09:13.072314Z", + "iopub.status.busy": "2024-02-29T16:09:13.071967Z", + "iopub.status.idle": "2024-02-29T16:09:14.279820Z", + "shell.execute_reply": "2024-02-29T16:09:14.278772Z", + "shell.execute_reply.started": "2024-02-29T16:09:13.072275Z" + } + }, + "outputs": [], + "source": [ + "# retrieve the object data via adler\n", + "# here we use an offline SQL database which contains the observations of the sso\n", + "fname = \"../tests/data/testing_database.db\"\n", + "planetoid = AdlerPlanetoid.construct_from_SQL(ssoid, sql_filename=fname)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d9a0623d-0dc7-49c1-99dd-a76ef970a3ff", + "metadata": { + "execution": { + "iopub.execute_input": "2024-02-29T16:09:14.281553Z", + "iopub.status.busy": "2024-02-29T16:09:14.281143Z", + "iopub.status.idle": "2024-02-29T16:09:14.287399Z", + "shell.execute_reply": "2024-02-29T16:09:14.286612Z", + "shell.execute_reply.started": "2024-02-29T16:09:14.281511Z" + } + }, + "outputs": [], + "source": [ + "# inspect the object\n", + "# planetoid.__dict__" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1d360360-025b-4a77-acf5-325b2f2d1873", + "metadata": { + "execution": { + "iopub.execute_input": "2024-02-29T16:09:14.290960Z", + "iopub.status.busy": "2024-02-29T16:09:14.290631Z", + "iopub.status.idle": "2024-02-29T16:09:14.307210Z", + "shell.execute_reply": "2024-02-29T16:09:14.306215Z", + "shell.execute_reply.started": "2024-02-29T16:09:14.290937Z" + } + }, + "outputs": [], + "source": [ + "planetoid.SSObject.__dict__" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "be8f8d63", + "metadata": {}, + "outputs": [], + "source": [ + "obs_r = planetoid.observations_in_filter(\"r\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "da1e483d", + "metadata": {}, + "outputs": [], + "source": [ + "obs_r.__dict__" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9d7dc125-06c1-49ad-8854-17d8c8b6954f", + "metadata": { + "execution": { + "iopub.execute_input": "2024-02-29T16:09:14.353538Z", + "iopub.status.busy": "2024-02-29T16:09:14.352944Z", + "iopub.status.idle": "2024-02-29T16:09:14.544075Z", + "shell.execute_reply": "2024-02-29T16:09:14.543137Z", + "shell.execute_reply.started": "2024-02-29T16:09:14.353500Z" + } + }, + "outputs": [], + "source": [ + "# plot the observations\n", + "x_plot = \"phaseAngle\"\n", + "y_plot = \"reduced_mag\"\n", + "\n", + "x = getattr(obs_r, x_plot)\n", + "y = getattr(obs_r, y_plot)\n", + "xerr = obs_r.magErr\n", + "\n", + "fig = plt.figure()\n", + "gs = gridspec.GridSpec(1, 1)\n", + "ax1 = plt.subplot(gs[0, 0])\n", + "\n", + "ax1.errorbar(x, y, xerr, fmt=\"o\")\n", + "\n", + "ax1.invert_yaxis()\n", + "ax1.set_xlabel(x_plot)\n", + "ax1.set_ylabel(y_plot)\n", + "\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6efe3b5a-09dd-4d5e-9f41-20ea6e1b43df", + "metadata": { + "execution": { + "iopub.execute_input": "2024-02-29T16:09:14.545627Z", + "iopub.status.busy": "2024-02-29T16:09:14.545342Z", + "iopub.status.idle": "2024-02-29T16:09:14.553323Z", + "shell.execute_reply": "2024-02-29T16:09:14.552540Z", + "shell.execute_reply.started": "2024-02-29T16:09:14.545603Z" + } + }, + "outputs": [], + "source": [ + "# define the phase curve\n", + "\n", + "r_H = planetoid.SSObject.H[2]\n", + "r_G12 = planetoid.SSObject.G12[2]\n", + "\n", + "pc = PhaseCurve(abs_mag=r_H * u.mag, phase_param=r_G12, model_name=\"HG12_Pen16\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "80f552f1-8907-4cc9-b57c-2e667eab459c", + "metadata": { + "execution": { + "iopub.execute_input": "2024-02-29T16:09:14.554644Z", + "iopub.status.busy": "2024-02-29T16:09:14.554319Z", + "iopub.status.idle": "2024-02-29T16:09:14.571588Z", + "shell.execute_reply": "2024-02-29T16:09:14.570921Z", + "shell.execute_reply.started": "2024-02-29T16:09:14.554620Z" + } + }, + "outputs": [], + "source": [ + "pc.model_function" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "24c1955e-95cd-4d77-ad05-aa5b8d18620a", + "metadata": { + "execution": { + "iopub.execute_input": "2024-02-29T16:09:14.572987Z", + "iopub.status.busy": "2024-02-29T16:09:14.572491Z", + "iopub.status.idle": "2024-02-29T16:09:14.593610Z", + "shell.execute_reply": "2024-02-29T16:09:14.592732Z", + "shell.execute_reply.started": "2024-02-29T16:09:14.572960Z" + } + }, + "outputs": [], + "source": [ + "alpha = np.linspace(0, np.amax(obs_r.phaseAngle)) * u.deg\n", + "alpha" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c3f30fe0-0d89-4ffa-8237-9c71181d44ee", + "metadata": { + "execution": { + "iopub.execute_input": "2024-02-29T16:09:14.594977Z", + "iopub.status.busy": "2024-02-29T16:09:14.594621Z", + "iopub.status.idle": "2024-02-29T16:09:14.614140Z", + "shell.execute_reply": "2024-02-29T16:09:14.613401Z", + "shell.execute_reply.started": "2024-02-29T16:09:14.594943Z" + } + }, + "outputs": [], + "source": [ + "red_mag = pc.ReducedMag(alpha)\n", + "red_mag" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "04be98a1-e4dc-4216-bcd9-ef777f6053fb", + "metadata": { + "execution": { + "iopub.execute_input": "2024-02-29T16:09:14.615656Z", + "iopub.status.busy": "2024-02-29T16:09:14.615050Z", + "iopub.status.idle": "2024-02-29T16:09:14.783515Z", + "shell.execute_reply": "2024-02-29T16:09:14.782664Z", + "shell.execute_reply.started": "2024-02-29T16:09:14.615623Z" + } + }, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "\n", + "# plot the observations with the LSST phase curve\n", + "x_plot = \"phaseAngle\"\n", + "y_plot = \"reduced_mag\"\n", + "\n", + "x = getattr(obs_r, x_plot)\n", + "y = getattr(obs_r, y_plot)\n", + "xerr = obs_r.magErr\n", + "\n", + "fig = plt.figure()\n", + "gs = gridspec.GridSpec(1, 1)\n", + "ax1 = plt.subplot(gs[0, 0])\n", + "\n", + "ax1.errorbar(x, y, xerr, fmt=\"o\")\n", + "\n", + "ax1.plot(alpha.value, red_mag.value)\n", + "\n", + "ax1.invert_yaxis()\n", + "ax1.set_xlabel(x_plot)\n", + "ax1.set_ylabel(y_plot)\n", + "\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9815543d-6140-4bdb-8bad-8296994723f4", + "metadata": { + "execution": { + "iopub.execute_input": "2024-02-29T16:09:14.784754Z", + "iopub.status.busy": "2024-02-29T16:09:14.784498Z", + "iopub.status.idle": "2024-02-29T16:09:14.953022Z", + "shell.execute_reply": "2024-02-29T16:09:14.952237Z", + "shell.execute_reply.started": "2024-02-29T16:09:14.784731Z" + } + }, + "outputs": [], + "source": [ + "# plot the observations\n", + "x_plot = \"midpointMjdTai\"\n", + "y_plot = \"reduced_mag\"\n", + "\n", + "x = getattr(obs_r, x_plot)\n", + "y = getattr(obs_r, y_plot)\n", + "xerr = obs_r.magErr\n", + "\n", + "fig = plt.figure()\n", + "gs = gridspec.GridSpec(1, 1)\n", + "ax1 = plt.subplot(gs[0, 0])\n", + "\n", + "ax1.errorbar(x, y, xerr, fmt=\"o\")\n", + "\n", + "ax1.invert_yaxis()\n", + "ax1.set_xlabel(x_plot)\n", + "ax1.set_ylabel(y_plot)\n", + "\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "de462b92-3914-4091-b0af-bddd9e9c1ef1", + "metadata": { + "execution": { + "iopub.execute_input": "2024-02-29T16:09:14.954181Z", + "iopub.status.busy": "2024-02-29T16:09:14.953935Z", + "iopub.status.idle": "2024-02-29T16:09:14.957556Z", + "shell.execute_reply": "2024-02-29T16:09:14.956913Z", + "shell.execute_reply.started": "2024-02-29T16:09:14.954159Z" + } + }, + "outputs": [], + "source": [ + "# do a different phase curve fit to the data\n", + "# adler should be able to fit different models, and perform more sophisticated fits" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f92891c9-6ccf-4dac-8887-9545f633ba90", + "metadata": { + "execution": { + "iopub.execute_input": "2024-02-29T16:09:14.963464Z", + "iopub.status.busy": "2024-02-29T16:09:14.958584Z", + "iopub.status.idle": "2024-02-29T16:09:14.975015Z", + "shell.execute_reply": "2024-02-29T16:09:14.974187Z", + "shell.execute_reply.started": "2024-02-29T16:09:14.963434Z" + } + }, + "outputs": [], + "source": [ + "pc_fit = PhaseCurve(abs_mag=pc.abs_mag, model_name=\"HG\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "db24432b-6d05-4ff2-9d98-e52d8c2e4342", + "metadata": { + "execution": { + "iopub.execute_input": "2024-02-29T16:09:14.976283Z", + "iopub.status.busy": "2024-02-29T16:09:14.975943Z", + "iopub.status.idle": "2024-02-29T16:09:14.992854Z", + "shell.execute_reply": "2024-02-29T16:09:14.992071Z", + "shell.execute_reply.started": "2024-02-29T16:09:14.976260Z" + } + }, + "outputs": [], + "source": [ + "pc_fit.model_function" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9039e2e2-27d9-4d21-b2f6-9504a5b85ce4", + "metadata": { + "execution": { + "iopub.execute_input": "2024-02-29T16:09:14.994511Z", + "iopub.status.busy": "2024-02-29T16:09:14.993863Z", + "iopub.status.idle": "2024-02-29T16:09:15.023285Z", + "shell.execute_reply": "2024-02-29T16:09:15.022583Z", + "shell.execute_reply.started": "2024-02-29T16:09:14.994486Z" + } + }, + "outputs": [], + "source": [ + "pc_fit.FitModel(\n", + " phase_angle=obs_r.phaseAngle * u.deg,\n", + " reduced_mag=obs_r.reduced_mag * u.mag,\n", + " mag_err=obs_r.magErr * u.mag,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5d4b7144-ee72-45e0-9606-c40f83c443c6", + "metadata": { + "execution": { + "iopub.execute_input": "2024-02-29T16:09:15.024555Z", + "iopub.status.busy": "2024-02-29T16:09:15.024229Z", + "iopub.status.idle": "2024-02-29T16:09:15.217263Z", + "shell.execute_reply": "2024-02-29T16:09:15.216519Z", + "shell.execute_reply.started": "2024-02-29T16:09:15.024533Z" + } + }, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "\n", + "# plot the observations with the LSST phase curve\n", + "x_plot = \"phaseAngle\"\n", + "y_plot = \"reduced_mag\"\n", + "\n", + "x = getattr(obs_r, x_plot)\n", + "y = getattr(obs_r, y_plot)\n", + "xerr = obs_r.magErr\n", + "\n", + "fig = plt.figure()\n", + "gs = gridspec.GridSpec(1, 1)\n", + "ax1 = plt.subplot(gs[0, 0])\n", + "\n", + "ax1.errorbar(x, y, xerr, fmt=\"o\")\n", + "\n", + "ax1.plot(alpha.value, pc.ReducedMag(alpha).value, label=pc.model_name)\n", + "ax1.plot(alpha.value, pc_fit.ReducedMag(alpha).value, label=pc_fit.model_name)\n", + "\n", + "ax1.invert_yaxis()\n", + "ax1.set_xlabel(x_plot)\n", + "ax1.set_ylabel(y_plot)\n", + "ax1.legend()\n", + "\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b7f39ed4-8334-4e10-a97c-a9471105225b", + "metadata": { + "execution": { + "iopub.execute_input": "2024-02-29T16:09:15.221317Z", + "iopub.status.busy": "2024-02-29T16:09:15.220742Z", + "iopub.status.idle": "2024-02-29T16:09:15.224414Z", + "shell.execute_reply": "2024-02-29T16:09:15.223697Z", + "shell.execute_reply.started": "2024-02-29T16:09:15.221290Z" + } + }, + "outputs": [], + "source": [ + "# # now we would add our calculated values back into planetoid\n", + "# planetoid.AdlerSchema.r_H = pc_fit.abs_mag\n", + "# planetoid.AdlerSchema.r_G = pc_fit.phase_param" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "adler-dev", + "language": "python", + "name": "adler-dev" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.13" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/src/adler/__init__.py b/src/adler/__init__.py index 25b7585..443fa66 100644 --- a/src/adler/__init__.py +++ b/src/adler/__init__.py @@ -1,2 +1,3 @@ from . import dataclasses from . import science +from . import utilities diff --git a/src/adler/dataclasses/AdlerPlanetoid.py b/src/adler/dataclasses/AdlerPlanetoid.py index fd6413a..060b0de 100644 --- a/src/adler/dataclasses/AdlerPlanetoid.py +++ b/src/adler/dataclasses/AdlerPlanetoid.py @@ -49,14 +49,56 @@ def __init__( self.AdlerData = adler_data @classmethod - def construct_from_SQL(cls): - # to-do - pass + def construct_from_SQL( + cls, + ssObjectId, + sql_filename, + filter_list=["u", "g", "r", "i", "z", "y"], + date_range=[60000.0, 67300.0], + schema=None, + ): + """Custom constructor which builds the AdlerPlanetoid object and the associated Observations, MPCORB and SSObject objects from + a local SQL database. Mostly used for testing. + + Parameters + ----------- + ssObjectId : str + ssObjectId of the object of interest. + + sql_filename : str + Filepath to the local SQL database. + + filter_list : list of str + A comma-separated list of the filters of interest. + + date_range : list of int + The minimum and maximum dates of the desired observations. + + schema : str or None + Schema/database from which to select the data tables. Can be None. Default is currently "dp03_catalogs_10yr" for testing using DP0.3. + + """ + + if len(date_range) != 2: + raise Exception("date_range argument must be of length 2.") + + observations_by_filter = cls.populate_observations( + cls, ssObjectId, filter_list, date_range, sql_filename=sql_filename, schema=schema + ) + + mpcorb = cls.populate_MPCORB(cls, ssObjectId, sql_filename=sql_filename, schema=schema) + ssobject = cls.populate_SSObject( + cls, ssObjectId, filter_list, sql_filename=sql_filename, schema=schema + ) + + adler_data = AdlerData(filter_list) + + return cls(ssObjectId, filter_list, date_range, observations_by_filter, mpcorb, ssobject, adler_data) @classmethod def construct_from_RSP( cls, ssObjectId, filter_list=["u", "g", "r", "i", "z", "y"], date_range=[60000.0, 67300.0] - ): + ): # pragma: no cover """Custom constructor which builds the AdlerPlanetoid object and the associated Observations, MPCORB and SSObject objects from the RSP. @@ -87,7 +129,15 @@ def construct_from_RSP( return cls(ssObjectId, filter_list, date_range, observations_by_filter, mpcorb, ssobject, adler_data) - def populate_observations(self, ssObjectId, filter_list, date_range, service=None, sql_filename=None): + def populate_observations( + self, + ssObjectId, + filter_list, + date_range, + service=None, + sql_filename=None, + schema="dp03_catalogs_10yr", + ): """Populates the observations_by_filter class attribute. Can populate from either the RSP for a SQL database: this behaviour is controlled by the service and sql_filename parameters, one of which must be supplied. @@ -102,27 +152,35 @@ def populate_observations(self, ssObjectId, filter_list, date_range, service=Non date_range : list of int The minimum and maximum dates of the desired observations. - service : pyvo.dal.tap.TAPService object + service : pyvo.dal.tap.TAPService object or None TAPService object linked to the RSP. Default=None. - sql_filename : str + sql_filename : str or None Filepath to a SQL database. Default=None. + schema : str or None + Schema/database from which to select the data tables. Can be None. Default is currently "dp03_catalogs_10yr" for testing using DP0.3. + """ + if schema: + schema = schema + "." + else: + schema = "" + observations_by_filter = [] for filter_name in filter_list: observations_sql_query = f""" SELECT - ssObject.ssObjectId, mag, magErr, band, midpointMjdTai, ra, dec, phaseAngle, + ssObject.ssObjectId, mag, magErr, band, midPointMjdTai, ra, dec, phaseAngle, topocentricDist, heliocentricDist FROM - dp03_catalogs_10yr.ssObject - JOIN dp03_catalogs_10yr.diaSource ON dp03_catalogs_10yr.ssObject.ssObjectId = dp03_catalogs_10yr.diaSource.ssObjectId - JOIN dp03_catalogs_10yr.ssSource ON dp03_catalogs_10yr.diaSource.diaSourceId = dp03_catalogs_10yr.ssSource.diaSourceId + {schema}ssObject + JOIN {schema}diaSource ON {schema}ssObject.ssObjectId = {schema}diaSource.ssObjectId + JOIN {schema}ssSource ON {schema}diaSource.diaSourceId = {schema}ssSource.diaSourceId WHERE - ssObject.ssObjectId = {ssObjectId} AND band = '{filter_name}' AND midpointMjdTai BETWEEN {date_range[0]} AND {date_range[1]} + ssObject.ssObjectId = {ssObjectId} AND band = '{filter_name}' AND midPointMjdTai BETWEEN {date_range[0]} AND {date_range[1]} """ data_table = get_data_table(observations_sql_query, service=service, sql_filename=sql_filename) @@ -133,7 +191,7 @@ def populate_observations(self, ssObjectId, filter_list, date_range, service=Non return observations_by_filter - def populate_MPCORB(self, ssObjectId, service=None, sql_filename=None): + def populate_MPCORB(self, ssObjectId, service=None, sql_filename=None, schema="dp03_catalogs_10yr"): """Populates the MPCORB object class attribute. Can populate from either the RSP for a SQL database: this behaviour is controlled by the service and sql_filename parameters, one of which must be supplied. @@ -142,19 +200,28 @@ def populate_MPCORB(self, ssObjectId, service=None, sql_filename=None): ssObjectId : str ssObjectId of the object of interest. - service : pyvo.dal.tap.TAPService object + service : pyvo.dal.tap.TAPService object or None TAPService object linked to the RSP. Default=None. - sql_filename : str + sql_filename : str or None Filepath to a SQL database. Default=None. + schema : str or None + Schema/database from which to select the data tables. Can be None. Default is currently "dp03_catalogs_10yr" for testing using DP0.3. + """ + + if schema: + schema = schema + "." + else: + schema = "" + MPCORB_sql_query = f""" SELECT ssObjectId, mpcDesignation, mpcNumber, mpcH, mpcG, epoch, peri, node, incl, e, n, q, uncertaintyParameter, flags FROM - dp03_catalogs_10yr.MPCORB + {schema}MPCORB WHERE ssObjectId = {ssObjectId} """ @@ -163,7 +230,9 @@ def populate_MPCORB(self, ssObjectId, service=None, sql_filename=None): return MPCORB.construct_from_data_table(ssObjectId, data_table) - def populate_SSObject(self, ssObjectId, filter_list, service=None, sql_filename=None): + def populate_SSObject( + self, ssObjectId, filter_list, service=None, sql_filename=None, schema="dp03_catalogs_10yr" + ): """Populates the SSObject class attribute. Can populate from either the RSP for a SQL database: this behaviour is controlled by the service and sql_filename parameters, one of which must be supplied. @@ -175,18 +244,26 @@ def populate_SSObject(self, ssObjectId, filter_list, service=None, sql_filename= filter_list : list of str A comma-separated list of the filters of interest. - service : pyvo.dal.tap.TAPService object + service : pyvo.dal.tap.TAPService object or None TAPService object linked to the RSP. Default=None. - sql_filename : str + sql_filename : str or None Filepath to a SQL database. Default=None. + schema : str or None + Schema/database from which to select the data tables. Can be None. Default is currently "dp03_catalogs_10yr" for testing using DP0.3. + """ + if schema: + schema = schema + "." + else: + schema = "" + filter_dependent_columns = "" for filter_name in filter_list: - filter_string = "{}_H, {}_G12, {}_Herr, {}_G12err, {}_nData, ".format( + filter_string = "{}_H, {}_G12, {}_HErr, {}_G12Err, {}_Ndata, ".format( filter_name, filter_name, filter_name, filter_name, filter_name ) @@ -198,7 +275,7 @@ def populate_SSObject(self, ssObjectId, filter_list, service=None, sql_filename= {filter_dependent_columns} maxExtendedness, minExtendedness, medianExtendedness FROM - dp03_catalogs_10yr.SSObject + {schema}SSObject WHERE ssObjectId = {ssObjectId} """ diff --git a/src/adler/dataclasses/Observations.py b/src/adler/dataclasses/Observations.py index 2d5aa1e..77768f6 100644 --- a/src/adler/dataclasses/Observations.py +++ b/src/adler/dataclasses/Observations.py @@ -52,15 +52,15 @@ class Observations: ssObjectId: str = "" filter_name: str = "" - mag: np.ndarray = field(default_factory=np.zeros(0)) - magErr: np.ndarray = field(default_factory=np.zeros(0)) - midpointMjdTai: np.ndarray = field(default_factory=np.zeros(0)) - ra: np.ndarray = field(default_factory=np.zeros(0)) - dec: np.ndarray = field(default_factory=np.zeros(0)) - phaseAngle: np.ndarray = field(default_factory=np.zeros(0)) - topocentricDist: np.ndarray = field(default_factory=np.zeros(0)) - heliocentricDist: np.ndarray = field(default_factory=np.zeros(0)) - reduced_mag: np.ndarray = field(default_factory=np.zeros(0)) + mag: np.ndarray = field(default_factory=lambda: np.zeros(0)) + magErr: np.ndarray = field(default_factory=lambda: np.zeros(0)) + midpointMjdTai: np.ndarray = field(default_factory=lambda: np.zeros(0)) + ra: np.ndarray = field(default_factory=lambda: np.zeros(0)) + dec: np.ndarray = field(default_factory=lambda: np.zeros(0)) + phaseAngle: np.ndarray = field(default_factory=lambda: np.zeros(0)) + topocentricDist: np.ndarray = field(default_factory=lambda: np.zeros(0)) + heliocentricDist: np.ndarray = field(default_factory=lambda: np.zeros(0)) + reduced_mag: np.ndarray = field(default_factory=lambda: np.zeros(0)) num_obs: int = 0 @classmethod @@ -85,21 +85,28 @@ def construct_from_data_table(cls, ssObjectId, filter_name, data_table): """ - reduced_mag = cls.calculate_reduced_mag( - cls, data_table["mag"], data_table["topocentricDist"], data_table["heliocentricDist"] - ) + mag = get_from_table(data_table, "mag", "array") + magErr = get_from_table(data_table, "magErr", "array") + midpointMjdTai = get_from_table(data_table, "midPointMjdTai", "array") + ra = get_from_table(data_table, "ra", "array") + dec = get_from_table(data_table, "dec", "array") + phaseAngle = get_from_table(data_table, "phaseAngle", "array") + topocentricDist = get_from_table(data_table, "topocentricDist", "array") + heliocentricDist = get_from_table(data_table, "heliocentricDist", "array") + + reduced_mag = cls.calculate_reduced_mag(cls, mag, topocentricDist, heliocentricDist) return cls( ssObjectId, filter_name, - data_table["mag"], - data_table["magErr"], - data_table["midpointMjdTai"], - data_table["ra"], - data_table["dec"], - data_table["phaseAngle"], - data_table["topocentricDist"], - data_table["heliocentricDist"], + mag, + magErr, + midpointMjdTai, + ra, + dec, + phaseAngle, + topocentricDist, + heliocentricDist, reduced_mag, len(data_table), ) diff --git a/src/adler/dataclasses/SSObject.py b/src/adler/dataclasses/SSObject.py index 0b1e1fd..d86414c 100644 --- a/src/adler/dataclasses/SSObject.py +++ b/src/adler/dataclasses/SSObject.py @@ -61,11 +61,11 @@ class SSObject: firstObservationDate: float = 0.0 arc: float = 0.0 numObs: int = 0 - H: np.ndarray = field(default_factory=np.zeros(0)) - G12: np.ndarray = field(default_factory=np.zeros(0)) - Herr: np.ndarray = field(default_factory=np.zeros(0)) - G12err: np.ndarray = field(default_factory=np.zeros(0)) - nData: np.ndarray = field(default_factory=np.zeros(0)) + H: np.ndarray = field(default_factory=lambda: np.zeros(0)) + G12: np.ndarray = field(default_factory=lambda: np.zeros(0)) + Herr: np.ndarray = field(default_factory=lambda: np.zeros(0)) + G12err: np.ndarray = field(default_factory=lambda: np.zeros(0)) + nData: np.ndarray = field(default_factory=lambda: np.zeros(0)) maxExtendedness: float = 0.0 minExtendedness: float = 0.0 medianExtendedness: float = 0.0 @@ -86,9 +86,9 @@ def construct_from_data_table(cls, ssObjectId, filter_list, data_table): for i, filter in enumerate(filter_list): H[i] = get_from_table(data_table, filter + "_H", "float") G12[i] = get_from_table(data_table, filter + "_G12", "float") - Herr[i] = get_from_table(data_table, filter + "_Herr", "float") - G12err[i] = get_from_table(data_table, filter + "_G12err", "float") - nData[i] = get_from_table(data_table, filter + "_nData", "int") + Herr[i] = get_from_table(data_table, filter + "_HErr", "float") + G12err[i] = get_from_table(data_table, filter + "_G12Err", "float") + nData[i] = get_from_table(data_table, filter + "_Ndata", "int") maxExtendedness = get_from_table(data_table, "maxExtendedness", "float") minExtendedness = get_from_table(data_table, "minExtendedness", "float") @@ -110,19 +110,3 @@ def construct_from_data_table(cls, ssObjectId, filter_list, data_table): minExtendedness, medianExtendedness, ) - - def populate_from_table(self, data_table): - """Populates the SSObject object from the data_table class variable created on initialisation.""" - - self.discoverySubmissionDate = self.get_from_table(data_table, "discoverySubmissionDate", "float") - self.firstObservationDate = self.get_from_table(data_table, "firstObservationDate", "float") - self.arc = self.get_from_table(data_table, "arc", "float") - self.numObs = self.get_from_table(data_table, "numObs", "int") - self.r_H = self.get_from_table(data_table, "r_H", "float") - self.r_G12 = self.get_from_table(data_table, "r_G12", "float") - self.r_Herr = self.get_from_table(data_table, "r_Herr", "float") - self.r_G12Err = self.get_from_table(data_table, "r_G12err", "float") - self.r_nData = self.get_from_table(data_table, "r_nData", "int") - self.maxExtendedness = self.get_from_table(data_table, "maxExtendedness", "float") - self.minExtendedness = self.get_from_table(data_table, "minExtendedness", "float") - self.medianExtendedness = self.get_from_table(data_table, "medianExtendedness", "float") diff --git a/src/adler/dataclasses/__init__.py b/src/adler/dataclasses/__init__.py new file mode 100644 index 0000000..9b7a7df --- /dev/null +++ b/src/adler/dataclasses/__init__.py @@ -0,0 +1,6 @@ +from . import AdlerData +from . import Observations +from . import dataclass_utilities +from . import MPCORB +from . import AdlerPlanetoid +from . import SSObject diff --git a/src/adler/dataclasses/dataclass_utilities.py b/src/adler/dataclasses/dataclass_utilities.py index 04372c3..9cbf89b 100644 --- a/src/adler/dataclasses/dataclass_utilities.py +++ b/src/adler/dataclasses/dataclass_utilities.py @@ -1,4 +1,7 @@ import numpy as np +import pandas as pd +import sqlite3 +import warnings def get_data_table(sql_query, service=None, sql_filename=None): @@ -11,16 +14,16 @@ def get_data_table(sql_query, service=None, sql_filename=None): sql_query : str The SQL query made to the RSP or SQL database. - service : pyvo.dal.tap.TAPService object + service : pyvo.dal.tap.TAPService object or None TAPService object linked to the RSP. Default=None. - sql_filename : str + sql_filename : str or None Filepath to a SQL database. Default=None. Returns ----------- - data_table : DALResultsTable + data_table : DALResultsTable or Pandas dataframe Data table containing the results of the SQL query. """ @@ -28,8 +31,19 @@ def get_data_table(sql_query, service=None, sql_filename=None): if service: data_table = service.search(sql_query) elif sql_filename: - # to-do - pass + cnx = sqlite3.connect(sql_filename) + data_table = pd.read_sql_query( + sql_query, cnx + ) # would really like to move away from Pandas for this... + + # Pandas is triggering a useless FutureWarning here which I am choosing to suppress. + # The code is already written to account for the FutureWarning, but it triggers anyway. Thanks, Pandas. + # Note that pd.option_context("future.no_silent_downcasting", True) would also work, but only for Pandas >2.0.3. + with warnings.catch_warnings(): + warnings.filterwarnings("ignore", category=FutureWarning) + data_table = data_table.fillna(value=np.nan).infer_objects( + copy=False + ) # changes Nones to NaNs because None forces dtype=object: bad. return data_table @@ -60,6 +74,8 @@ def get_from_table(data_table, column_name, data_type): elif data_type == "array": return np.array(data_table[column_name]) else: - print("Type not recognised.") + raise TypeError( + "Type for argument data_type not recognised: must be one of 'str', 'float', 'int', 'array'." + ) except ValueError: raise ValueError("Could not cast column name to type.") diff --git a/src/adler/science/__init__.py b/src/adler/science/__init__.py new file mode 100644 index 0000000..3a3d202 --- /dev/null +++ b/src/adler/science/__init__.py @@ -0,0 +1 @@ +from . import PhaseCurve diff --git a/src/adler/utilities/__init__.py b/src/adler/utilities/__init__.py new file mode 100644 index 0000000..13dbd9e --- /dev/null +++ b/src/adler/utilities/__init__.py @@ -0,0 +1 @@ +from . import tests_utilities diff --git a/src/adler/utilities/tests_utilities.py b/src/adler/utilities/tests_utilities.py new file mode 100644 index 0000000..6f4d76c --- /dev/null +++ b/src/adler/utilities/tests_utilities.py @@ -0,0 +1,42 @@ +import os +from pathlib import Path + + +def get_test_data_path(): # pragma: no cover + """Gets the absolute path of the tests/data directory. + + Returns + ----------- + path_to_data : str + The absolute path to the tests/data directory. + + """ + + # where is this file? + path_to_file = os.path.abspath(__file__) + + # the test data folder is thus: + path_to_data = os.path.join(str(Path(path_to_file).parents[3]), "tests/data/") + + return path_to_data + + +def get_test_data_filepath(filename): # pragma: no cover + """Gets the absolute path of a supplied file in the tests/data directory. + + + Parameters + ----------- + filename : str + The filename of the desired test file. + + Returns + ----------- + str + The absolute path to the specified file in the tests/data directory. + + """ + + filepath = get_test_data_path() + + return os.path.join(filepath, filename) diff --git a/tests/adler/dataclasses/test_AdlerPlanetoid.py b/tests/adler/dataclasses/test_AdlerPlanetoid.py new file mode 100644 index 0000000..a92b777 --- /dev/null +++ b/tests/adler/dataclasses/test_AdlerPlanetoid.py @@ -0,0 +1,107 @@ +import numpy as np +from numpy.testing import assert_almost_equal + +from adler.utilities.tests_utilities import get_test_data_filepath +from adler.dataclasses.AdlerPlanetoid import AdlerPlanetoid + + +ssoid = 8268570668335894776 +test_db_path = get_test_data_filepath("testing_database.db") + + +def test_construct_from_SQL(): + test_planetoid = AdlerPlanetoid.construct_from_SQL(ssoid, test_db_path) + + # testing just a few values here to ensure correct setup: these objects have their own unit tests + assert test_planetoid.MPCORB.mpcH == 19.8799991607666 + assert test_planetoid.SSObject.discoverySubmissionDate == 60218.0 + assert_almost_equal( + test_planetoid.observations_by_filter[1].mag, + [ + 21.33099937, + 22.67099953, + 23.5359993, + 22.85000038, + 22.97599983, + 22.94499969, + 23.13599968, + 23.19400024, + 23.1609993, + ], + ) + + # did we pick up all the filters? + assert len(test_planetoid.observations_by_filter) == 6 + assert len(test_planetoid.SSObject.H) == 6 + assert test_planetoid.filter_list == ["u", "g", "r", "i", "z", "y"] + + # checking the date range to ensure it's the default + assert test_planetoid.date_range == [60000.0, 67300.0] + + +def test_construct_with_single_filter(): + test_planetoid = AdlerPlanetoid.construct_from_SQL(ssoid, test_db_path, filter_list=["g"]) + + # should only be one filter in here now + assert len(test_planetoid.observations_by_filter) == 1 + assert len(test_planetoid.SSObject.H) == 1 + assert test_planetoid.filter_list == ["g"] + + assert_almost_equal( + test_planetoid.observations_by_filter[0].mag, + [ + 21.33099937, + 22.67099953, + 23.5359993, + 22.85000038, + 22.97599983, + 22.94499969, + 23.13599968, + 23.19400024, + 23.1609993, + ], + ) + + +def test_construct_with_date_range(): + test_planetoid = AdlerPlanetoid.construct_from_SQL( + ssoid, test_db_path, filter_list=["r"], date_range=[61000.0, 62000.0] + ) + + expected_dates = np.array( + [ + 61294.15865, + 61322.07319, + 61323.00925, + 61326.03134, + 61329.00043, + 61330.01524, + 61355.02232, + 61355.02277, + 61253.97055, + 61253.96744, + 61253.96433, + 61052.13729, + ] + ) + + assert_almost_equal(test_planetoid.observations_by_filter[0].midpointMjdTai, expected_dates) + + +def test_observations_in_filter(): + test_planetoid = AdlerPlanetoid.construct_from_SQL(ssoid, test_db_path) + + assert_almost_equal( + test_planetoid.observations_in_filter("g").mag, + [ + 21.33099937, + 22.67099953, + 23.5359993, + 22.85000038, + 22.97599983, + 22.94499969, + 23.13599968, + 23.19400024, + 23.1609993, + ], + ) diff --git a/tests/adler/dataclasses/test_Observations.py b/tests/adler/dataclasses/test_Observations.py new file mode 100644 index 0000000..24c2b25 --- /dev/null +++ b/tests/adler/dataclasses/test_Observations.py @@ -0,0 +1,183 @@ +from numpy.testing import assert_almost_equal +import numpy as np + +from adler.dataclasses.Observations import Observations +from adler.dataclasses.dataclass_utilities import get_data_table +from adler.utilities.tests_utilities import get_test_data_filepath + + +def test_construct_observations_from_data_table(): + ssoid = 8268570668335894776 + test_db_path = get_test_data_filepath("testing_database.db") + filter_name = "r" + date_range = [61000.0, 62000.0] + + test_query = f""" + SELECT + ssObject.ssObjectId, mag, magErr, band, midPointMjdTai, ra, dec, phaseAngle, + topocentricDist, heliocentricDist + FROM + ssObject + JOIN diaSource ON ssObject.ssObjectId = diaSource.ssObjectId + JOIN ssSource ON diaSource.diaSourceId = ssSource.diaSourceId + WHERE + ssObject.ssObjectId = {ssoid} AND band = '{filter_name}' AND midPointMjdTai BETWEEN {date_range[0]} AND {date_range[1]} + """ + + data_table = get_data_table(test_query, sql_filename=test_db_path) + test_observations = Observations.construct_from_data_table(ssoid, filter_name, data_table) + + expected_mag = np.array( + [ + 20.4470005, + 22.24799919, + 22.27599907, + 22.29100037, + 22.66200066, + 22.69799995, + 23.56800079, + 23.70199966, + 22.22900009, + 22.04899979, + 22.17700005, + 23.52199936, + ] + ) + expected_magerr = np.array( + [0.011, 0.046, 0.06, 0.082, 0.083, 0.063, 0.17200001, 0.199, 0.264, 0.28299999, 0.31600001, 0.163] + ) + expected_mjd = np.array( + [ + 61294.15865, + 61322.07319, + 61323.00925, + 61326.03134, + 61329.00043, + 61330.01524, + 61355.02232, + 61355.02277, + 61253.97055, + 61253.96744, + 61253.96433, + 61052.13729, + ] + ) + expected_ra = np.array( + [ + 301.5682143, + 315.2799036, + 315.6390597, + 316.7793075, + 317.8804823, + 318.2527231, + 327.0967385, + 327.0968451, + 170.4323598, + 170.4247983, + 170.4172297, + 62.6767601, + ] + ) + expected_dec = np.array( + [ + -17.709034, + -13.7156598, + -13.6098197, + -13.2712987, + -12.9420707, + -12.8300092, + -10.035292, + -10.0351984, + -3.6550378, + -3.6513877, + -3.6478445, + 27.0535373, + ] + ) + expected_phaseangle = np.array( + [ + 33.56268311, + 32.00559616, + 31.97466469, + 31.86707687, + 31.74268913, + 31.69605255, + 29.76596832, + 29.76592445, + 126.7827301, + 126.78705597, + 126.79136658, + 18.63666534, + ] + ) + expected_heliodist = np.array( + [ + 1.35723567, + 1.66025329, + 1.66984999, + 1.70058703, + 1.73042095, + 1.74053574, + 1.97701716, + 1.97702122, + 0.87671232, + 0.87667543, + 0.87663853, + 2.21820784, + ] + ) + expected_topodist = np.array( + [ + 0.45957258, + 0.93472457, + 0.95206416, + 1.00859618, + 1.06491303, + 1.08432984, + 1.58487654, + 1.58488584, + 0.20776999, + 0.20780498, + 0.20783997, + 1.4200809, + ] + ) + expected_reduced_mag = np.array( + [ + 21.47195362, + 21.29370916, + 21.2692807, + 21.11941945, + 21.33467112, + 21.31877818, + 21.08797141, + 21.22195309, + 25.92680044, + 25.74652589, + 25.87425196, + 21.03042275, + ] + ) + + assert_almost_equal(test_observations.mag, expected_mag) + assert_almost_equal(test_observations.magErr, expected_magerr) + assert_almost_equal(test_observations.midpointMjdTai, expected_mjd) + assert_almost_equal(test_observations.ra, expected_ra) + assert_almost_equal(test_observations.dec, expected_dec) + assert_almost_equal(test_observations.phaseAngle, expected_phaseangle) + assert_almost_equal(test_observations.heliocentricDist, expected_heliodist) + assert_almost_equal(test_observations.topocentricDist, expected_topodist) + assert_almost_equal(test_observations.reduced_mag, expected_reduced_mag) + + +def test_calculate_reduced_mag(): + test_object = Observations( + mag=np.array([20]), topocentricDist=np.array([0.5]), heliocentricDist=np.array([1.5]) + ) + reduced_mag = test_object.calculate_reduced_mag( + test_object.mag, test_object.topocentricDist, test_object.heliocentricDist + )[0] + + expected_reduced_mag = 20.0 - 5 * np.log10(0.5 * 1.5) + + assert_almost_equal(reduced_mag, expected_reduced_mag) diff --git a/tests/adler/dataclasses/test_SSObject.py b/tests/adler/dataclasses/test_SSObject.py new file mode 100644 index 0000000..f4121e4 --- /dev/null +++ b/tests/adler/dataclasses/test_SSObject.py @@ -0,0 +1,52 @@ +from numpy.testing import assert_almost_equal +from numpy.testing import assert_equal +import numpy as np + +from adler.utilities.tests_utilities import get_test_data_filepath +from adler.dataclasses.dataclass_utilities import get_data_table +from adler.dataclasses.SSObject import SSObject + + +def test_construct_SSObject_from_data_table(): + ssoid = 8268570668335894776 + test_db_path = get_test_data_filepath("testing_database.db") + + filter_list = ["r", "g"] + + filter_dependent_columns = "" + + for filter_name in filter_list: + filter_string = "{}_H, {}_G12, {}_HErr, {}_G12Err, {}_Ndata, ".format( + filter_name, filter_name, filter_name, filter_name, filter_name + ) + + filter_dependent_columns += filter_string + + test_query = f""" + SELECT + discoverySubmissionDate, firstObservationDate, arc, numObs, + {filter_dependent_columns} + maxExtendedness, minExtendedness, medianExtendedness + FROM + SSObject + WHERE + ssObjectId = {ssoid} + """ + + data_table = get_data_table(test_query, sql_filename=test_db_path) + test_SSObject = SSObject.construct_from_data_table(ssoid, filter_list, data_table) + + assert test_SSObject.ssObjectId == 8268570668335894776 + assert test_SSObject.filter_list == filter_list + assert_almost_equal(test_SSObject.discoverySubmissionDate, 60218.0, decimal=6) + assert_almost_equal(test_SSObject.firstObservationDate, 60220.01958, decimal=6) + assert_almost_equal(test_SSObject.arc, 3342.05859375, decimal=6) + assert test_SSObject.numObs == 94 + assert_almost_equal(test_SSObject.H, [19.80589294, 20.29232597], decimal=6) + assert_almost_equal(test_SSObject.G12, [1.52932608, 1.72339332], decimal=6) + assert_almost_equal(test_SSObject.Herr, [0.01974303, 0.0302103], decimal=6) + assert_almost_equal(test_SSObject.G12err, [0.05071714, 0.04049731], decimal=6) + assert_equal(test_SSObject.nData, [38.0, 9.0]) + assert_equal(test_SSObject.maxExtendedness, 0.0) + assert_equal(test_SSObject.minExtendedness, 0.0) + assert_equal(test_SSObject.medianExtendedness, 0.0) diff --git a/tests/adler/dataclasses/test_dataclass_utilities.py b/tests/adler/dataclasses/test_dataclass_utilities.py new file mode 100644 index 0000000..dfae4b7 --- /dev/null +++ b/tests/adler/dataclasses/test_dataclass_utilities.py @@ -0,0 +1,56 @@ +import pytest +import pandas as pd +from pandas.testing import assert_frame_equal +from numpy.testing import assert_equal + +from adler.dataclasses.dataclass_utilities import get_data_table +from adler.dataclasses.dataclass_utilities import get_from_table +from adler.utilities.tests_utilities import get_test_data_filepath + + +def test_get_data_table(): + ssoid = 8268570668335894776 + test_db_path = get_test_data_filepath("testing_database.db") + filter_name = "r" + date_range = [61000.0, 62000.0] + + test_query = f""" + SELECT + ssObject.ssObjectId, mag, magErr, band, midPointMjdTai, ra, dec, phaseAngle, + topocentricDist, heliocentricDist + FROM + ssObject + JOIN diaSource ON ssObject.ssObjectId = diaSource.ssObjectId + JOIN ssSource ON diaSource.diaSourceId = ssSource.diaSourceId + WHERE + ssObject.ssObjectId = {ssoid} AND band = '{filter_name}' AND midPointMjdTai BETWEEN {date_range[0]} AND {date_range[1]} + """ + + data_table = get_data_table(test_query, sql_filename=test_db_path) + + expected_table = pd.read_csv(get_test_data_filepath("test_dataclass_utilities_table.csv")) + assert_frame_equal(data_table, expected_table) + + +def test_get_from_table(): + test_table = pd.DataFrame( + {"string_col": "a test string", "int_col": 4, "float_col": 4.5, "array_col": [5, 6]} + ) + + assert get_from_table(test_table, "string_col", "str") == "a test string" + assert get_from_table(test_table, "int_col", "int") == 4 + assert get_from_table(test_table, "float_col", "float") == 4.5 + assert_equal(get_from_table(test_table, "array_col", "array"), [5, 6]) + + with pytest.raises(ValueError) as error_info_1: + get_from_table(test_table, "string_col", "int") + + assert error_info_1.value.args[0] == "Could not cast column name to type." + + with pytest.raises(TypeError) as error_info_2: + get_from_table(test_table, "string_col", "fake") + + assert ( + error_info_2.value.args[0] + == "Type for argument data_type not recognised: must be one of 'str', 'float', 'int', 'array'." + ) diff --git a/tests/adler/test_MPCORB.py b/tests/adler/test_MPCORB.py new file mode 100644 index 0000000..f94db75 --- /dev/null +++ b/tests/adler/test_MPCORB.py @@ -0,0 +1,40 @@ +from numpy.testing import assert_almost_equal +import numpy as np + +from adler.dataclasses.MPCORB import MPCORB +from adler.dataclasses.dataclass_utilities import get_data_table +from adler.utilities.tests_utilities import get_test_data_filepath + + +def test_construct_MPCORB_from_data_table(): + ssoid = 8268570668335894776 + test_db_path = get_test_data_filepath("testing_database.db") + + test_query = f""" + SELECT + ssObjectId, mpcDesignation, mpcNumber, mpcH, mpcG, epoch, peri, node, incl, e, n, q, + uncertaintyParameter, flags + FROM + MPCORB + WHERE + ssObjectId = {ssoid} + """ + + data_table = get_data_table(test_query, sql_filename=test_db_path) + test_MPCORB = MPCORB.construct_from_data_table(ssoid, data_table) + + assert test_MPCORB.ssObjectId == 8268570668335894776 + assert test_MPCORB.mpcDesignation == "2014 QL4" + assert test_MPCORB.mpcNumber == 0 + assert_almost_equal(test_MPCORB.mpcH, 19.8799991607666, decimal=6) + assert_almost_equal(test_MPCORB.mpcG, 0.15000000596046448, decimal=6) + assert_almost_equal(test_MPCORB.epoch, 60065.0, decimal=6) + assert_almost_equal(test_MPCORB.peri, 260.5468204162153, decimal=6) + assert_almost_equal(test_MPCORB.node, 322.8059, decimal=6) + assert_almost_equal(test_MPCORB.incl, 4.427569999999975, decimal=6) + assert_almost_equal(test_MPCORB.e, 0.7168805704972735, decimal=6) + assert_almost_equal(test_MPCORB.e, 0.7168805704972735, decimal=6) + assert np.isnan(test_MPCORB.n) + assert_almost_equal(test_MPCORB.q, 0.5898291078470536, decimal=6) + assert test_MPCORB.uncertaintyParameter == "" + assert test_MPCORB.flags == "0" diff --git a/tests/data/test_dataclass_utilities_table.csv b/tests/data/test_dataclass_utilities_table.csv new file mode 100644 index 0000000..11a4dfc --- /dev/null +++ b/tests/data/test_dataclass_utilities_table.csv @@ -0,0 +1,13 @@ +ssObjectId,mag,magErr,band,midPointMjdTai,ra,dec,phaseAngle,topocentricDist,heliocentricDist +8268570668335894776,20.44700050354004,0.010999999940395355,r,61294.15865,301.5682143,-17.709034,33.56268310546875,0.45957258343696594,1.3572356700897217 +8268570668335894776,22.24799919128418,0.04600000008940697,r,61322.07319,315.2799036,-13.7156598,32.00559616088867,0.9347245693206787,1.6602532863616943 +8268570668335894776,22.275999069213867,0.05999999865889549,r,61323.00925,315.6390597,-13.6098197,31.97466468811035,0.9520641565322876,1.6698499917984009 +8268570668335894776,22.291000366210938,0.0820000022649765,r,61326.03134,316.7793075,-13.2712987,31.867076873779297,1.0085961818695068,1.7005870342254639 +8268570668335894776,22.66200065612793,0.08299999684095383,r,61329.00043,317.8804823,-12.9420707,31.74268913269043,1.064913034439087,1.7304209470748901 +8268570668335894776,22.697999954223633,0.06300000101327896,r,61330.01524,318.2527231,-12.8300092,31.69605255126953,1.0843298435211182,1.7405357360839844 +8268570668335894776,23.56800079345703,0.1720000058412552,r,61355.02232,327.0967385,-10.035292,29.765968322753906,1.584876537322998,1.9770171642303467 +8268570668335894776,23.70199966430664,0.19900000095367432,r,61355.02277,327.0968451,-10.0351984,29.76592445373535,1.584885835647583,1.9770212173461914 +8268570668335894776,22.229000091552734,0.2639999985694885,r,61253.97055,170.4323598,-3.6550378,126.78273010253906,0.2077699899673462,0.8767123222351074 +8268570668335894776,22.048999786376953,0.28299999237060547,r,61253.96744,170.4247983,-3.6513877,126.78705596923828,0.20780497789382935,0.8766754269599915 +8268570668335894776,22.177000045776367,0.3160000145435333,r,61253.96433,170.4172297,-3.6478445,126.79136657714844,0.2078399658203125,0.8766385316848755 +8268570668335894776,23.52199935913086,0.16300000250339508,r,61052.13729,62.6767601,27.0535373,18.63666534423828,1.4200809001922607,2.218207836151123 diff --git a/tests/data/testing_database.db b/tests/data/testing_database.db new file mode 100644 index 0000000..7a220e3 Binary files /dev/null and b/tests/data/testing_database.db differ