From 6c988965298d7dd598b47a5bf00236bc30f3c1a5 Mon Sep 17 00:00:00 2001 From: Rahil Makadia Date: Wed, 17 Jul 2024 10:55:59 -0700 Subject: [PATCH 01/12] opik total derivatives --- grss/prop/prop_utils.py | 3 +- grss/version.txt | 2 +- include/simulation.h | 2 + src/approach.cpp | 103 ++++++++++++++++++++++++++++++++-------- src/grss.cpp | 8 +++- src/pck.cpp | 2 +- src/spk.cpp | 8 ++-- 7 files changed, 100 insertions(+), 28 deletions(-) diff --git a/grss/prop/prop_utils.py b/grss/prop/prop_utils.py index 2b02fc40..b9e193cb 100644 --- a/grss/prop/prop_utils.py +++ b/grss/prop/prop_utils.py @@ -360,7 +360,7 @@ def partials_to_ellipse(ca, au2units, n_std, print_ellipse_params, units, analyt part_tlin_minus_t = ca.dTLinMinusT + extra_zeros part_tlin = part_tlin_minus_t @ stm_tca_tmap part_t = ca.dt + extra_zeros - # part_tlin += part_t + part_tlin += part_t part_kizner = np.zeros((3,stm_tca_tmap.shape[0])) part_kizner[:2,:] = np.array([ca.kizner.dx+extra_zeros,ca.kizner.dy+extra_zeros])@stm_tca_tmap part_kizner[2,:] = part_tlin @@ -369,6 +369,7 @@ def partials_to_ellipse(ca, au2units, n_std, print_ellipse_params, units, analyt part_scaled[2,:] = part_tlin part_opik = np.zeros((3,stm_tca_tmap.shape[0])) part_opik[:2,:] = np.array([ca.opik.dx+extra_zeros,ca.opik.dy+extra_zeros])@stm_tca_tmap + part_opik[:2,:6] += ca.dOpikTotalDerivTerm2 part_opik[2,:] = part_tlin part_mtp = np.zeros((3,stm_tca_tmap.shape[0])) part_mtp[:2,:] = np.array([ca.mtp.dx+extra_zeros,ca.mtp.dy+extra_zeros])@stm_tca_tmap diff --git a/grss/version.txt b/grss/version.txt index 9d086c6d..b673f6ac 100644 --- a/grss/version.txt +++ b/grss/version.txt @@ -1 +1 @@ -4.1.4 \ No newline at end of file +4.1.5 \ No newline at end of file diff --git a/include/simulation.h b/include/simulation.h index bb1b3208..270004e5 100644 --- a/include/simulation.h +++ b/include/simulation.h @@ -298,6 +298,7 @@ struct BPlaneParameters { * @param mtp Modified Target Plane parameters. * @param dTLinMinusT Partial derivatives of the (linearized intersection minus map) time with respect to the state at the close approach. * @param dt Partial derivatives of the time of closest approach with respect to the state at the close approach. + * @param dOpikTotalDerivTerm2 Partial derivatives of the Öpik total derivative term 2 (non-constant planet velocity). */ class CloseApproachParameters { private: @@ -328,6 +329,7 @@ class CloseApproachParameters { BPlaneParameters mtp; std::vector dTLinMinusT = std::vector(6, 0.0L); std::vector dt = std::vector(6, 0.0L); + std::vector< std::vector > dOpikTotalDerivTerm2 = std::vector< std::vector >(2, std::vector(6, 0.0L)); /** * @brief Get the close approach parameters. */ diff --git a/src/approach.cpp b/src/approach.cpp index 0e808109..0d4b2633 100644 --- a/src/approach.cpp +++ b/src/approach.cpp @@ -468,9 +468,9 @@ void CloseApproachParameters::get_ca_parameters(PropSimulation *propSim, const r for (size_t k = 0; k < 3; k++) { vCentralBodyHelio[k] = xCentralBody[3+k]-xSun[3+k]; } - real xiHat[3], zetaHat[3], vCentralBodyHelioCrossSHat[3]; - vcross(vCentralBodyHelio, sHat, vCentralBodyHelioCrossSHat); - vunit(vCentralBodyHelioCrossSHat, 3, xiHat); + real xiHat[3], zetaHat[3], vPlanetCrossSHatVec[3]; + vcross(vCentralBodyHelio, sHat, vPlanetCrossSHatVec); + vunit(vPlanetCrossSHatVec, 3, xiHat); vcross(sHat, xiHat, zetaHat); for (size_t k = 0; k < 3; k++) { zetaHat[k] *= -1; @@ -655,26 +655,76 @@ void CloseApproachParameters::get_ca_parameters(PropSimulation *propSim, const r this->scaled.dy[k] = (this->kizner.dy[k] - this->scaled.y*partial_lambda[k])/this->gravFocusFactor; } - // // Acceleration of the Sun is not included in the calculation - // // because it is negligibly small - // real accSun[3]; - // memset(accSun, 0, 3*sizeof(real)); - // for (size_t k = 0; k < propSim->integParams.nSpice; k++) { - // if (propSim->spiceBodies[k].spiceId == 10) { - // accSun[0] = propSim->spiceBodies[k].acc[0]; - // accSun[1] = propSim->spiceBodies[k].acc[1]; - // accSun[2] = propSim->spiceBodies[k].acc[2]; - // break; - // } - // } - real **partial_vel_planet = new real*[6]; + // Acceleration of the Sun is not included in the calculation + real accSun[3]; + for (size_t k = 0; k < propSim->integParams.nSpice; k++) { + if (propSim->spiceBodies[k].spiceId == 10) { + accSun[0] = propSim->spiceBodies[k].acc[0]; + accSun[1] = propSim->spiceBodies[k].acc[1]; + accSun[2] = propSim->spiceBodies[k].acc[2]; + break; + } + } + real **partial_vel_planet = new real*[3]; + for (size_t k = 0; k < 3; k++) { + partial_vel_planet[k] = new real[6]; + for (size_t k2 = 0; k2 < 6; k2++) { + partial_vel_planet[k][k2] = this->dt[k2]*(accPlanet[k] - accSun[k]); + } + } + // partials of xi and zeta w.r.t planet velocity are needed for total derivative + real **partial_vpl_vpl = new real*[3]; + for (size_t k = 0; k < 3; k++) { + partial_vpl_vpl[k] = new real[3]; + for (size_t k2 = 0; k2 < 3; k2++) { + partial_vpl_vpl[k][k2] = 0; + } + partial_vpl_vpl[k][k] = 1; + } + real vPlanetCrossSHat; + vnorm(vPlanetCrossSHatVec, 3, vPlanetCrossSHat); + real **partial_vPlanetCrossSHatVec_vpl = new real*[3]; + for (size_t k = 0; k < 3; k++) { + partial_vPlanetCrossSHatVec_vpl[k] = new real[3]; + vcross(partial_vpl_vpl[k], sHat, partial_vPlanetCrossSHatVec_vpl[k]); + } + real *partial_vPlanetCrossSHat_vpl = new real[3]; + vcross(xiHat, sHat, partial_vPlanetCrossSHat_vpl); + for (size_t k = 0; k < 3; k++){ + partial_vPlanetCrossSHat_vpl[k] *= -1.0; + } + real **partial_xiHat_vpl = new real*[3]; + real **partial_zetaHat_vpl = new real*[3]; + for (size_t k = 0; k < 3; k++) { + partial_xiHat_vpl[k] = new real[3]; + for (size_t k2 = 0; k2 < 3; k2++) { + partial_xiHat_vpl[k][k2] = + (vPlanetCrossSHat * partial_vPlanetCrossSHatVec_vpl[k][k2] - + vPlanetCrossSHatVec[k2] * partial_vPlanetCrossSHat_vpl[k]) / + vPlanetCrossSHat / vPlanetCrossSHat; + } + partial_zetaHat_vpl[k] = new real[3]; + vcross(sHat, partial_xiHat_vpl[k], partial_zetaHat_vpl[k]); + for (size_t k2 = 0; k2 < 3; k2++) { + partial_zetaHat_vpl[k][k2] *= -1.0; + } + } + real *partial_xi_vpl = new real[3]; + real *partial_zeta_vpl = new real[3]; + for (size_t k = 0; k < 3; k++) { + vdot(partial_zetaHat_vpl[k], hVec, 3, partial_xi_vpl[k]); + partial_xi_vpl[k] /= vInf; + vdot(partial_xiHat_vpl[k], hVec, 3, partial_zeta_vpl[k]); + partial_zeta_vpl[k] /= -vInf; + } for (size_t k = 0; k < 6; k++) { - partial_vel_planet[k] = new real[3]; for (size_t k2 = 0; k2 < 3; k2++) { - // partial_vel_planet[k][k2] = this->dt[k]*(accPlanet[k2] - accSun[k2]); - partial_vel_planet[k][k2] = this->dt[k]*accPlanet[k2]; + temp1Vec[k2] = partial_vel_planet[k2][k]; } + vdot(partial_xi_vpl, temp1Vec, 3, this->dOpikTotalDerivTerm2[0][k]); + vdot(partial_zeta_vpl, temp1Vec, 3, this->dOpikTotalDerivTerm2[1][k]); } + real **partial_xiHat = new real*[6]; real temp3, temp3Vec[3]; real temp4, temp4Vec[3]; @@ -723,10 +773,16 @@ void CloseApproachParameters::get_ca_parameters(PropSimulation *propSim, const r delete[] partial_rHat[k]; delete[] partial_sHat[k]; delete[] partial_tHat[k]; - delete[] partial_vel_planet[k]; delete[] partial_xiHat[k]; delete[] partial_zetaHat[k]; } + for (size_t k = 0; k < 3; k++) { + delete[] partial_vel_planet[k]; + delete[] partial_vpl_vpl[k]; + delete[] partial_vPlanetCrossSHatVec_vpl[k]; + delete[] partial_xiHat_vpl[k]; + delete[] partial_zetaHat_vpl[k]; + } delete[] partial_alpha; delete[] partial_vInf; delete[] partial_a; @@ -742,6 +798,13 @@ void CloseApproachParameters::get_ca_parameters(PropSimulation *propSim, const r delete[] partial_tHat; delete[] partial_lambda; delete[] partial_vel_planet; + delete[] partial_vpl_vpl; + delete[] partial_vPlanetCrossSHatVec_vpl; + delete[] partial_vPlanetCrossSHat_vpl; + delete[] partial_xiHat_vpl; + delete[] partial_zetaHat_vpl; + delete[] partial_xi_vpl; + delete[] partial_zeta_vpl; delete[] partial_xiHat; delete[] partial_zetaHat; } diff --git a/src/grss.cpp b/src/grss.cpp index db8a7558..e4637960 100644 --- a/src/grss.cpp +++ b/src/grss.cpp @@ -202,7 +202,8 @@ PYBIND11_MODULE(libgrss, m) { R"mydelimiter( Name of the central body. )mydelimiter") - .def_readwrite("centralBodyIdx", &CloseApproachParameters::centralBodyIdx, + .def_readwrite("centralBodyIdx", + &CloseApproachParameters::centralBodyIdx, R"mydelimiter( Index of the central body. )mydelimiter") @@ -252,6 +253,11 @@ PYBIND11_MODULE(libgrss, m) { .def_readwrite("dt", &CloseApproachParameters::dt, R"mydelimiter( Partials of time of periapsis with respect to CA state. )mydelimiter") + .def_readwrite("dOpikTotalDerivTerm2", + &CloseApproachParameters::dOpikTotalDerivTerm2, + R"mydelimiter( + Partial derivatives of the Öpik total derivative term 2 (non-constant planet velocity). + )mydelimiter") .def("get_ca_parameters", &CloseApproachParameters::get_ca_parameters, py::arg("propSim"), py::arg("tMap"), R"mydelimiter( Calculate the close approach parameters. diff --git a/src/pck.cpp b/src/pck.cpp index 51590015..9a458507 100644 --- a/src/pck.cpp +++ b/src/pck.cpp @@ -24,7 +24,7 @@ void pck_free(PckInfo* bpc) { free(bpc->targets); } munmap(bpc->map, bpc->len); - memset(bpc, 0, sizeof(PckInfo)); + memset((void *)bpc, 0, sizeof(PckInfo)); free(bpc); bpc = nullptr; } diff --git a/src/spk.cpp b/src/spk.cpp index 6a1a49f6..3ae50639 100644 --- a/src/spk.cpp +++ b/src/spk.cpp @@ -24,7 +24,7 @@ void spk_free(SpkInfo* bsp) { free(bsp->targets); } munmap(bsp->map, bsp->len); - memset(bsp, 0, sizeof(SpkInfo)); + memset((void *)bsp, 0, sizeof(SpkInfo)); free(bsp); bsp = nullptr; } @@ -227,17 +227,17 @@ void spk_calc(SpkInfo *bsp, double epoch, int spiceId, double *state) { U[0] = 0.0; U[1] = 0.0; U[2] = 4.0; - for (size_t p = 2; p < P; p++) { + for (int p = 2; p < P; p++) { T[p] = 2.0 * z * T[p - 1] - T[p - 2]; S[p] = 2.0 * z * S[p - 1] + 2.0 * T[p - 1] - S[p - 2]; } - for (size_t p = 3; p < P; p++) { + for (int p = 3; p < P; p++) { U[p] = 2.0 * z * U[p - 1] + 4.0 * S[p - 1] - U[p - 2]; } const double c = 1.0 / val[1]; // derivative scaling factor from SPICE/spke02.f and chbint.f for (size_t i = 0; i < 3; i++) { const int b = 2 + i * P; - for (size_t p = 0; p < P; p++) { + for (int p = 0; p < P; p++) { const double v = val[b + p]; state[i] += v * T[p] / 149597870.7; state[i + 3] += v * S[p] * c / 149597870.7 * 86400.0; From 5448fe3f899cb16f62f42abd903f7fcbb10c10a0 Mon Sep 17 00:00:00 2001 From: Rahil Makadia Date: Mon, 12 Aug 2024 15:52:19 -0500 Subject: [PATCH 02/12] code; joss updates --- grss/fit/fit_optical.py | 8 ++++++-- grss/fit/fit_simulation.py | 8 +++++--- grss/prop/prop_utils.py | 9 +++++---- joss/joss_paper.bib | 15 +++++++++++++++ joss/joss_paper.md | 4 ++-- 5 files changed, 33 insertions(+), 11 deletions(-) diff --git a/grss/fit/fit_optical.py b/grss/fit/fit_optical.py index d756b771..a396b8bb 100644 --- a/grss/fit/fit_optical.py +++ b/grss/fit/fit_optical.py @@ -952,7 +952,8 @@ def eliminate_obs(obs_df, max_obs_per_night, verbose): def get_optical_obs(body_id, optical_obs_file=None, t_min_tdb=None, t_max_tdb=None, debias_lowres=True, deweight=True, - eliminate=False, num_obs_per_night=5, verbose=False): + eliminate=False, num_obs_per_night=5, verbose=False, + accept_weights=False): """ Assemble the optical observations for a given body into an array for an orbit fit. @@ -977,6 +978,8 @@ def get_optical_obs(body_id, optical_obs_file=None, t_min_tdb=None, Effective/Maximum number of effective observations per night, by default 5 verbose : bool, optional Flag to print out information about the observations, by default False + accept_weights : bool, optional + Flag to accept the weights from the input file, by default False Returns ------- @@ -1004,7 +1007,8 @@ def get_optical_obs(body_id, optical_obs_file=None, t_min_tdb=None, "Setting biases to zero.") opt_idx = obs_df.query("mode != 'RAD'").index obs_df.loc[opt_idx, ['biasRA', 'biasDec']] = 0.0 - obs_df = apply_weighting_scheme(obs_df, verbose) + if not accept_weights: + obs_df = apply_weighting_scheme(obs_df, verbose) if deweight: obs_df = deweight_obs(obs_df, num_obs_per_night, verbose) elif eliminate: diff --git a/grss/fit/fit_simulation.py b/grss/fit/fit_simulation.py index ed370c3e..430b4387 100644 --- a/grss/fit/fit_simulation.py +++ b/grss/fit/fit_simulation.py @@ -1727,15 +1727,17 @@ def filter_lsq(self, verbose=True): self._check_convergence(delta_x) if self.converged: if self.reject_outliers and start_rejecting: - print(f"Converged after rejecting outliers. Rejected {self.num_rejected} out", - f"of {len(self.optical_idx)} optical observations.") + if verbose: + print(f"Converged after rejecting outliers. Rejected {self.num_rejected}", + f"out of {len(self.optical_idx)} optical observations.") break msg = "Converged without rejecting outliers." if self.reject_outliers: msg += " Starting outlier rejection now..." start_rejecting = True self.converged = False - print(msg) + if verbose: + print(msg) if not self.reject_outliers: break if self.n_iter == self.n_iter_max and not self.converged: diff --git a/grss/prop/prop_utils.py b/grss/prop/prop_utils.py index b9e193cb..7f87effa 100644 --- a/grss/prop/prop_utils.py +++ b/grss/prop/prop_utils.py @@ -866,9 +866,10 @@ def plot_earth_impact(impact_list, print_ellipse_params=False, sigma_points=None size = zoom_size*1e3 m = Basemap(width=size,height=size, rsphere=(6378137.00,6356752.3142), - resolution='l',area_thresh=size/100,projection='lcc', + resolution='i',area_thresh=size/100,projection='lcc', lat_0=mean_lat,lon_0=mean_lon) - m.shadedrelief() + m.bluemarble() + m.drawcountries() lat_step = lon_step = 3 if zoom_size <= 1e3 else 10 if zoom_size <= 500: lat_step = lon_step = 1 @@ -894,10 +895,10 @@ def plot_earth_impact(impact_list, print_ellipse_params=False, sigma_points=None x_lon,y_lat = m(lon,lat) m.plot(x_lon,y_lat,'r.') lat_half = 'N' if mean_lat > 0 else 'S' + if save_name is not None: + plt.savefig(save_name, dpi=500, bbox_inches='tight') plt.suptitle('Impact Location at 100km altitude w.r.t Earth: ' f'{mean_lat:0.2f}$^o${lat_half}, {mean_lon:0.2f}$^o$E', y=0.93) - if save_name is not None: - plt.savefig(save_name, dpi=500, bbox_inches='tight') plt.show() return None diff --git a/joss/joss_paper.bib b/joss/joss_paper.bib index 8a48c912..aac87f1d 100644 --- a/joss/joss_paper.bib +++ b/joss/joss_paper.bib @@ -1,3 +1,18 @@ +@article{Makadia2022, +doi = {10.3847/PSJ/ac7de7}, +url = {https://dx.doi.org/10.3847/PSJ/ac7de7}, +year = {2022}, +month = {aug}, +publisher = {The American Astronomical Society}, +volume = {3}, +number = {8}, +pages = {184}, +author = {Rahil Makadia and Sabina D. Raducan and Eugene G. Fahnestock and Siegfried Eggl}, +title = {Heliocentric Effects of the DART Mission on the (65803) Didymos Binary Asteroid System}, +journal = {The Planetary Science Journal}, +abstract = {The Double Asteroid Redirect Test (DART) is NASA’s first kinetic impact–based asteroid deflection mission. The DART spacecraft will act as a projectile during a hypervelocity impact on Dimorphos, the secondary asteroid in the (65803) Didymos binary system, and alter its mutual orbital period. The initial momentum transfer between the DART spacecraft and Dimorphos is enhanced by the ejecta flung off the surface of Dimorphos. This exchange is characterized within the system by the momentum enhancement parameter, β, and on a heliocentric level by its counterpart, β ⊙. The relationship between β and the physical characteristics of Dimorphos is discussed here. A nominal set of Dimorphos physical parameters from the design reference asteroid and impact circumstances from the design reference mission are used to initialize the ejecta particles for dynamical propagation. The results of this propagation are translated into a gradual momentum transfer onto the Didymos system barycenter. A high-quality solar system propagator is then used to produce precise estimates of the post-DART encounters between Didymos and Earth by generating updated close approach maps. Results show that even for an unexpectedly high β ⊙, a collision between the Didymos system and Earth is practically excluded in the foreseeable future. A small but significant difference is found in modeling the overall momentum transfer when individual ejecta particles escape the Didymos system, as opposed to imparting the ejecta momentum as a single impulse at impact. This difference has implications for future asteroid deflection campaigns, especially when it is necessary to steer asteroids away from gravitational keyholes.} +} + @article{Makadia2024, author = {Rahil Makadia and Steven R. Chesley and Davide Farnocchia and Shantanu P. Naidu and Damya Souami and Paolo Tanga and Kleomenis Tsiganis and Masatoshi Hirabayashi and Siegfried Eggl}, journal = {The Planetary Science Journal}, diff --git a/joss/joss_paper.md b/joss/joss_paper.md index a1c435e8..2dbb35d0 100644 --- a/joss/joss_paper.md +++ b/joss/joss_paper.md @@ -40,12 +40,12 @@ Understanding the motion of small solar system bodies is of utmost importance wh In this paper, we present ``GRSS``, the Gauss-Radau Small-body Simulator, an open-source library for orbit determination and propagation of small bodies in the solar system. ``GRSS`` is an open-source, MIT licensed software library with a C++ foundation and a Python binding. The propagator is based on the ``IAS15`` algorithm, a 15th order integrator based on Gauss-Radau quadrature [@Rein2014]. Only the particles of interest are integrated within ``GRSS`` to reduce computational cost. The states for the planets and Big16 main-belt asteroids are computed using memory-mapped SPICE digital ephemeris kernels as done by @Holman2023 in the ``ASSIST`` orbit propagator library. In addition to the propagator, the C++ portion of the library also has the ability to detect impacts and calculate close encounter circumstances using various formulations of the B-plane [@Kizner1961; @Opik1976; @Chodas1999; @Milani1999]. -The C++ functionality is then exposed to Python through a binding generated using ``pybind11`` [@pybind11]. The Python layer of ``GRSS`` uses the propagator as the foundation to compute the orbits of small bodies from a given set of optical and/or radar astrometry from the Minor Planet Center, the JPL Small Body Radar Astrometry database, and the Gaia Focused Product Release (FPR) solar system observations database. Additionally, the orbit determination modules also have the ability to fit especially demanding measurements such as stellar occultations. These capabilities of the ``GRSS`` library have already been used to study the measurability of the heliocentric changes in the orbit of the (65803) Didymos binary asteroid system as a result of the DART impact [@Makadia2024]. +The C++ functionality is then exposed to Python through a binding generated using ``pybind11`` [@pybind11]. The Python layer of ``GRSS`` uses the propagator as the foundation to compute the orbits of small bodies from a given set of optical and/or radar astrometry from the Minor Planet Center, the JPL Small Body Radar Astrometry database, and the Gaia Focused Product Release (FPR) solar system observations database. Additionally, the orbit determination modules also have the ability to fit especially demanding measurements such as stellar occultations. These capabilities of the ``GRSS`` library have already been used to study the the heliocentric changes in the orbit of the (65803) Didymos binary asteroid system as a result of the DART impact [@Makadia2022,@Makadia2024]. ``GRSS`` will continue to be developed in the future, with anticipated contributions including the ability to perform impact monitoring and kinetic impact deflection studies. ``GRSS`` is available to the community through the Python Package Index (PyPI) and the source code is available on GitHub. This availability will allow the research community to have access to a reliable and efficient tool for studying the dynamics of small bodies in the solar system. # Acknowledgements -R.M. acknowledges support from a NASA Space Technology Graduate Research Opportunities (NSTGRO) Fellowship, contract No. 80NSSC22K1173. The work of S.R.C. and D.F. was carried out at the Jet Propulsion Laboratory, California Institute of Technology, under contract No. 80NM0018D0004 with NASA. +R.M. acknowledges funding from a NASA Space Technology Graduate Research Opportunities (NSTGRO) award, NASA contract No. 80NSSC22K1173. The work of S.R.C. and D.F. was carried out at the Jet Propulsion Laboratory, California Institute of Technology, under a contract with the National Aeronautics and Space Administration (No. 80NM0018D0004). This work has made use of data from the European Space Agency (ESA) mission Gaia, processed by the Gaia Data Processing and Analysis Consortium. This research has also extensively used data and services provided by the International Astronomical Union Minor Planet Center. Data from the MPC's database is made freely available to the public. Funding for this data and the MPC's operations comes from a NASA PDCO grant (80NSSC22M0024), administered via a University of Maryland - SAO subaward (106075-Z6415201). The MPC's computing equipment is funded in part by the above award, and in part by funding from the Tamkin Foundation. # References From 24a31ae6ea18d5dd0786c22b17a8bf46080c2e96 Mon Sep 17 00:00:00 2001 From: Rahil Makadia Date: Mon, 12 Aug 2024 15:54:17 -0500 Subject: [PATCH 03/12] update joss citations --- joss/joss_paper.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/joss/joss_paper.md b/joss/joss_paper.md index 2dbb35d0..b8a4a0a1 100644 --- a/joss/joss_paper.md +++ b/joss/joss_paper.md @@ -40,7 +40,7 @@ Understanding the motion of small solar system bodies is of utmost importance wh In this paper, we present ``GRSS``, the Gauss-Radau Small-body Simulator, an open-source library for orbit determination and propagation of small bodies in the solar system. ``GRSS`` is an open-source, MIT licensed software library with a C++ foundation and a Python binding. The propagator is based on the ``IAS15`` algorithm, a 15th order integrator based on Gauss-Radau quadrature [@Rein2014]. Only the particles of interest are integrated within ``GRSS`` to reduce computational cost. The states for the planets and Big16 main-belt asteroids are computed using memory-mapped SPICE digital ephemeris kernels as done by @Holman2023 in the ``ASSIST`` orbit propagator library. In addition to the propagator, the C++ portion of the library also has the ability to detect impacts and calculate close encounter circumstances using various formulations of the B-plane [@Kizner1961; @Opik1976; @Chodas1999; @Milani1999]. -The C++ functionality is then exposed to Python through a binding generated using ``pybind11`` [@pybind11]. The Python layer of ``GRSS`` uses the propagator as the foundation to compute the orbits of small bodies from a given set of optical and/or radar astrometry from the Minor Planet Center, the JPL Small Body Radar Astrometry database, and the Gaia Focused Product Release (FPR) solar system observations database. Additionally, the orbit determination modules also have the ability to fit especially demanding measurements such as stellar occultations. These capabilities of the ``GRSS`` library have already been used to study the the heliocentric changes in the orbit of the (65803) Didymos binary asteroid system as a result of the DART impact [@Makadia2022,@Makadia2024]. +The C++ functionality is then exposed to Python through a binding generated using ``pybind11`` [@pybind11]. The Python layer of ``GRSS`` uses the propagator as the foundation to compute the orbits of small bodies from a given set of optical and/or radar astrometry from the Minor Planet Center, the JPL Small Body Radar Astrometry database, and the Gaia Focused Product Release (FPR) solar system observations database. Additionally, the orbit determination modules also have the ability to fit especially demanding measurements such as stellar occultations. These capabilities of the ``GRSS`` library have already been used to study the the heliocentric changes in the orbit of the (65803) Didymos binary asteroid system as a result of the DART impact [@Makadia2022; @Makadia2024]. ``GRSS`` will continue to be developed in the future, with anticipated contributions including the ability to perform impact monitoring and kinetic impact deflection studies. ``GRSS`` is available to the community through the Python Package Index (PyPI) and the source code is available on GitHub. This availability will allow the research community to have access to a reliable and efficient tool for studying the dynamics of small bodies in the solar system. From 026861810a58ef8eec8b63cd5edd6efc85914870 Mon Sep 17 00:00:00 2001 From: Rahil Makadia Date: Mon, 12 Aug 2024 15:56:23 -0500 Subject: [PATCH 04/12] update joss workflow --- .github/workflows/joss.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/joss.yml b/.github/workflows/joss.yml index 7523fccf..1fcd8de0 100644 --- a/.github/workflows/joss.yml +++ b/.github/workflows/joss.yml @@ -20,7 +20,7 @@ jobs: journal: joss paper-path: ./joss/joss_paper.md - name: Upload - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@master with: name: paper path: ./joss/paper.pdf From ab75d0649e6397353e17a0ece2e765227fcbed4f Mon Sep 17 00:00:00 2001 From: Rahil Makadia Date: Mon, 12 Aug 2024 16:34:45 -0500 Subject: [PATCH 05/12] updated impact plotting --- grss/prop/prop_utils.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/grss/prop/prop_utils.py b/grss/prop/prop_utils.py index 7f87effa..8ebaed51 100644 --- a/grss/prop/prop_utils.py +++ b/grss/prop/prop_utils.py @@ -895,6 +895,8 @@ def plot_earth_impact(impact_list, print_ellipse_params=False, sigma_points=None x_lon,y_lat = m(lon,lat) m.plot(x_lon,y_lat,'r.') lat_half = 'N' if mean_lat > 0 else 'S' + if mean_lat < 0: + mean_lat = np.abs(mean_lat) if save_name is not None: plt.savefig(save_name, dpi=500, bbox_inches='tight') plt.suptitle('Impact Location at 100km altitude w.r.t Earth: ' From 9061d6ae7994e5632a467b4ec5202847134dee13 Mon Sep 17 00:00:00 2001 From: Rahil Makadia Date: Wed, 14 Aug 2024 15:41:42 -0500 Subject: [PATCH 06/12] update conda env yaml --- environment.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/environment.yml b/environment.yml index b7197a3c..0d2af62c 100644 --- a/environment.yml +++ b/environment.yml @@ -8,4 +8,4 @@ dependencies: - jupyter - ipykernel - pip: - -r requirements.txt + - -r requirements.txt From 08c04ca9ee5ec119ca2ed008161fe0e545744fc1 Mon Sep 17 00:00:00 2001 From: Rahil Makadia Date: Wed, 14 Aug 2024 19:01:41 -0500 Subject: [PATCH 07/12] update paper --- joss/joss_paper.md | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/joss/joss_paper.md b/joss/joss_paper.md index b8a4a0a1..65bd6101 100644 --- a/joss/joss_paper.md +++ b/joss/joss_paper.md @@ -26,7 +26,7 @@ affiliations: index: 1 - name: Jet Propulsion Laboratory, California Institute of Technology, Pasadena, CA 91109, USA index: 2 -date: XX April 2024 +date: XX August 2024 bibliography: joss_paper.bib aas-doi: 10.3847/PSJ/xxxxx aas-journal: Planetary Science Journal @@ -34,18 +34,23 @@ aas-journal: Planetary Science Journal # Statement of Need -Understanding the motion of small solar system bodies is of utmost importance when looking at the problem through the lens of planetary defense. The ability to compute the orbit of an asteroid or a comet from various observations and then predicting the body's motion in the future is critical in understanding its impact hazard. The NASA Center for Near-Earth Object Studies (CNEOS) has developed a suite of state-of-the-art tools over the course of decades for this specific purpose. However, these tools are not publicly available. With the expected increase in the number of Near-Earth Object (NEO) observations as well as discoveries when new observatories such as the Vera C. Rubin Observatory come online [@Schwamb2023], there is a need for a publicly available library that can reliably perform both orbit propagation and determination for asteroids and comets. +Understanding the motion of small solar system bodies is of utmost importance when looking at the problem through the lens of planetary defense. The ability to compute the orbit of an asteroid or a comet from various observations and then predicting its trajectory in the future is critical to understanding its impact hazard. The NASA Center for Near-Earth Object Studies (CNEOS) at the Jet Propulsion Laboratory (JPL) has developed a suite of state-of-the-art tools over the course of decades for this specific purpose. However, these tools are not publicly available. With the expected increase in the number of Near-Earth Object (NEO) observations as well as discoveries when new observatories such as the Vera C. Rubin Observatory come online [@Schwamb2023], there is a need for a publicly available library that can reliably perform both orbit determination and propagation for small bodies in the solar system. # Summary -In this paper, we present ``GRSS``, the Gauss-Radau Small-body Simulator, an open-source library for orbit determination and propagation of small bodies in the solar system. ``GRSS`` is an open-source, MIT licensed software library with a C++ foundation and a Python binding. The propagator is based on the ``IAS15`` algorithm, a 15th order integrator based on Gauss-Radau quadrature [@Rein2014]. Only the particles of interest are integrated within ``GRSS`` to reduce computational cost. The states for the planets and Big16 main-belt asteroids are computed using memory-mapped SPICE digital ephemeris kernels as done by @Holman2023 in the ``ASSIST`` orbit propagator library. In addition to the propagator, the C++ portion of the library also has the ability to detect impacts and calculate close encounter circumstances using various formulations of the B-plane [@Kizner1961; @Opik1976; @Chodas1999; @Milani1999]. +In this paper, we present ``GRSS``, the Gauss-Radau Small-body Simulator, an open-source library for orbit determination and propagation of small bodies in the solar system. ``GRSS`` is an open-source, MIT licensed software library with a C++11 foundation and a Python binding. The propagator is based on the ``IAS15`` algorithm, a 15th order integrator based on Gauss-Radau quadrature [@Rein2014]. Only the particles of interest are integrated within ``GRSS`` to reduce computational cost. The states for the planets and Big16 main-belt asteroids are computed using memory-mapped SPICE digital ephemeris kernels as done by @Holman2023 in the ``ASSIST`` orbit propagator library. In addition to the propagator, the C++ portion of the library also has the ability to predict impacts and calculate close encounter circumstances using various formulations of the B-plane [@Kizner1961; @Opik1976; @Chodas1999; @Milani1999]. -The C++ functionality is then exposed to Python through a binding generated using ``pybind11`` [@pybind11]. The Python layer of ``GRSS`` uses the propagator as the foundation to compute the orbits of small bodies from a given set of optical and/or radar astrometry from the Minor Planet Center, the JPL Small Body Radar Astrometry database, and the Gaia Focused Product Release (FPR) solar system observations database. Additionally, the orbit determination modules also have the ability to fit especially demanding measurements such as stellar occultations. These capabilities of the ``GRSS`` library have already been used to study the the heliocentric changes in the orbit of the (65803) Didymos binary asteroid system as a result of the DART impact [@Makadia2022; @Makadia2024]. +The C++ functionality is exposed to Python through a binding generated using ``pybind11`` [@pybind11]. The Python layer of ``GRSS`` uses the propagator as the foundation to compute the orbits of small bodies from a given set of optical and/or radar astrometry from the Minor Planet Center[^1], the JPL Small Body Radar Astrometry database[^2], and the Gaia Focused Product Release (FPR) solar system observations database[^3]. Additionally, the orbit determination modules also have the ability to fit especially demanding measurements such as stellar occultations. These capabilities of the ``GRSS`` library have already been used to study the the heliocentric changes in the orbit of the (65803) Didymos binary asteroid system as a result of the DART impact [@Makadia2022; @Makadia2024]. -``GRSS`` will continue to be developed in the future, with anticipated contributions including the ability to perform impact monitoring and kinetic impact deflection studies. ``GRSS`` is available to the community through the Python Package Index (PyPI) and the source code is available on GitHub. This availability will allow the research community to have access to a reliable and efficient tool for studying the dynamics of small bodies in the solar system. +``GRSS`` will continue to be developed in the future, with anticipated contributions including the ability to perform mission studies for future asteroid deflections. ``GRSS`` is publicly available to the community through the Python Package Index (PyPI) and the source code is available on GitHub[^4]. Therefore, ``GRSS`` will allow the research community to have access to a reliable and efficient tool for studying the dynamics of small bodies in the solar system. + +[^1]: +[^2]: +[^3]: +[^4]: # Acknowledgements -R.M. acknowledges funding from a NASA Space Technology Graduate Research Opportunities (NSTGRO) award, NASA contract No. 80NSSC22K1173. The work of S.R.C. and D.F. was carried out at the Jet Propulsion Laboratory, California Institute of Technology, under a contract with the National Aeronautics and Space Administration (No. 80NM0018D0004). This work has made use of data from the European Space Agency (ESA) mission Gaia, processed by the Gaia Data Processing and Analysis Consortium. This research has also extensively used data and services provided by the International Astronomical Union Minor Planet Center. Data from the MPC's database is made freely available to the public. Funding for this data and the MPC's operations comes from a NASA PDCO grant (80NSSC22M0024), administered via a University of Maryland - SAO subaward (106075-Z6415201). The MPC's computing equipment is funded in part by the above award, and in part by funding from the Tamkin Foundation. +R.M. acknowledges funding from a NASA Space Technology Graduate Research Opportunities (NSTGRO) award, NASA contract No. 80NSSC22K1173. The work of S.R.C. and D.F. was carried out at the Jet Propulsion Laboratory, California Institute of Technology, under a contract with the National Aeronautics and Space Administration (No. 80NM0018D0004). This work has made use of data from the European Space Agency (ESA) mission Gaia, processed by the Gaia Data Processing and Analysis Consortium. This library has also extensively used data and services provided by the International Astronomical Union Minor Planet Center. Data from the MPC's database is made freely available to the public. Funding for this data and the MPC's operations comes from a NASA PDCO grant (80NSSC22M0024), administered via a University of Maryland - SAO subaward (106075-Z6415201). The MPC's computing equipment is funded in part by the above award, and in part by funding from the Tamkin Foundation. # References From e4d114ddc95cf30e7944acc41817424347b1cc1a Mon Sep 17 00:00:00 2001 From: Rahil Makadia Date: Wed, 14 Aug 2024 19:03:39 -0500 Subject: [PATCH 08/12] update title --- joss/joss_paper.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/joss/joss_paper.md b/joss/joss_paper.md index 65bd6101..ac53aca3 100644 --- a/joss/joss_paper.md +++ b/joss/joss_paper.md @@ -1,12 +1,12 @@ --- -title: 'GRSS: An open-source small-body science tool for planetary defense' +title: 'Gauss-Radau Small-body Simulator (GRSS): An Open-Source Library for Planetary Defense' tags: - Python - C++ - Asteroids - Comets - Orbit Determination - - Orbit Propagator + - Orbit Propagation authors: - name: Rahil Makadia corresponding: true From 654bededd89e016144e4917fd2157d42fb60ffcd Mon Sep 17 00:00:00 2001 From: Rahil Makadia Date: Wed, 14 Aug 2024 19:12:05 -0500 Subject: [PATCH 09/12] fix reference for joss --- joss/joss_paper.bib | 9 +-------- joss/joss_paper.md | 13 +++++++------ 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/joss/joss_paper.bib b/joss/joss_paper.bib index aac87f1d..b1981578 100644 --- a/joss/joss_paper.bib +++ b/joss/joss_paper.bib @@ -71,13 +71,6 @@ @inproceedings{Chodas1999 url = {https://hdl.handle.net/2014/16816} } -@misc{pybind11, -author = {Wenzel Jakob and Jason Rhinelander and Dean Moldovan}, -year = {2017}, -url = {https://github.com/pybind/pybind11}, -title = {pybind11 -- Seamless operability between C++11 and Python} -} - @article{Milani1999, title = {The Asteroid Identification Problem: II. Target Plane Confidence Boundaries}, journal = {Icarus}, @@ -86,7 +79,7 @@ @article{Milani1999 pages = {408-423}, year = {1999}, issn = {0019-1035}, -doi = {https://doi.org/10.1006/icar.1999.6135}, +doi = {10.1006/icar.1999.6135}, author = {Andrea Milani and Giovanni B. Valsecchi}, } diff --git a/joss/joss_paper.md b/joss/joss_paper.md index ac53aca3..bd568555 100644 --- a/joss/joss_paper.md +++ b/joss/joss_paper.md @@ -40,14 +40,15 @@ Understanding the motion of small solar system bodies is of utmost importance wh In this paper, we present ``GRSS``, the Gauss-Radau Small-body Simulator, an open-source library for orbit determination and propagation of small bodies in the solar system. ``GRSS`` is an open-source, MIT licensed software library with a C++11 foundation and a Python binding. The propagator is based on the ``IAS15`` algorithm, a 15th order integrator based on Gauss-Radau quadrature [@Rein2014]. Only the particles of interest are integrated within ``GRSS`` to reduce computational cost. The states for the planets and Big16 main-belt asteroids are computed using memory-mapped SPICE digital ephemeris kernels as done by @Holman2023 in the ``ASSIST`` orbit propagator library. In addition to the propagator, the C++ portion of the library also has the ability to predict impacts and calculate close encounter circumstances using various formulations of the B-plane [@Kizner1961; @Opik1976; @Chodas1999; @Milani1999]. -The C++ functionality is exposed to Python through a binding generated using ``pybind11`` [@pybind11]. The Python layer of ``GRSS`` uses the propagator as the foundation to compute the orbits of small bodies from a given set of optical and/or radar astrometry from the Minor Planet Center[^1], the JPL Small Body Radar Astrometry database[^2], and the Gaia Focused Product Release (FPR) solar system observations database[^3]. Additionally, the orbit determination modules also have the ability to fit especially demanding measurements such as stellar occultations. These capabilities of the ``GRSS`` library have already been used to study the the heliocentric changes in the orbit of the (65803) Didymos binary asteroid system as a result of the DART impact [@Makadia2022; @Makadia2024]. +The C++ functionality is exposed to Python through a binding generated using ``pybind11``[^1]. The Python layer of ``GRSS`` uses the propagator as the foundation to compute the orbits of small bodies from a given set of optical and/or radar astrometry from the Minor Planet Center[^2], the JPL Small Body Radar Astrometry database[^3], and the Gaia Focused Product Release (FPR) solar system observations database[^4]. Additionally, the orbit determination modules also have the ability to fit especially demanding measurements such as stellar occultations. These capabilities of the ``GRSS`` library have already been used to study the the heliocentric changes in the orbit of the (65803) Didymos binary asteroid system as a result of the DART impact [@Makadia2022; @Makadia2024]. -``GRSS`` will continue to be developed in the future, with anticipated contributions including the ability to perform mission studies for future asteroid deflections. ``GRSS`` is publicly available to the community through the Python Package Index (PyPI) and the source code is available on GitHub[^4]. Therefore, ``GRSS`` will allow the research community to have access to a reliable and efficient tool for studying the dynamics of small bodies in the solar system. +``GRSS`` will continue to be developed in the future, with anticipated contributions including the ability to perform mission studies for future asteroid deflections. ``GRSS`` is publicly available to the community through the Python Package Index (PyPI) and the source code is available on GitHub[^5]. Therefore, ``GRSS`` will allow the research community to have access to a reliable and efficient tool for studying the dynamics of small bodies in the solar system. -[^1]: -[^2]: -[^3]: -[^4]: +[^1]: +[^2]: +[^3]: +[^4]: +[^5]: # Acknowledgements From 641a5f3be2a2a530dbc7172d0bfc76471fc7a45f Mon Sep 17 00:00:00 2001 From: Rahil Makadia Date: Sat, 17 Aug 2024 19:36:51 -0500 Subject: [PATCH 10/12] OD sim updates --- grss/fit/fit_simulation.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/grss/fit/fit_simulation.py b/grss/fit/fit_simulation.py index 430b4387..0ca6ba4a 100644 --- a/grss/fit/fit_simulation.py +++ b/grss/fit/fit_simulation.py @@ -563,7 +563,11 @@ def __init__(self, x_init, obs_df, cov_init=None, n_iter_max=10, self.fixed_propsim_params['events'] = events if events is not None else [] self.reject_outliers = True self.reject_criteria = [3.0, 2.8] - self.num_rejected = 0 + # number of rejected obs is the count of ['D', 'd'] in selAst + sel_ast = self.obs['selAst'].values + num_auto_rejected = np.sum(sel_ast == 'D') + num_force_rejected = np.sum(sel_ast == 'd') + self.num_rejected = num_auto_rejected + num_force_rejected self.converged = False return None @@ -1525,7 +1529,6 @@ def _get_rms_and_reject_outliers(self, partials, residuals, start_rejecting): "Default values are chi_reject=3.0 and chi_recover=2.8 " "(Implemented as FitSimulation.reject_criteria=[3.0, 2.8])") full_cov = self.covariance - self.num_rejected = 0 rms_u = 0 chi_sq = 0 j = 0 @@ -1556,9 +1559,10 @@ def _get_rms_and_reject_outliers(self, partials, residuals, start_rejecting): # outlier rejection, only reject RA/Dec measurements if size == 2: if residual_chi_squared > chi_reject**2 and sel_ast[i] not in {'a', 'd'}: + if sel_ast[i] == 'A': + self.num_rejected += 1 sel_ast[i] = 'D' - self.num_rejected += 1 - elif sel_ast[i] == 'D' and residual_chi_squared < chi_recover**2: + elif residual_chi_squared < chi_recover**2 and sel_ast[i] == 'D': sel_ast[i] = 'A' self.num_rejected -= 1 if sel_ast[i] not in {'D', 'd'}: @@ -1614,7 +1618,7 @@ def _check_convergence(self, delta_x): """ # check for convergence based on weighted rms if self.n_iter > 1: - del_rms_convergence = 1e-3 + del_rms_convergence = 1e-4 curr_rms = self.iters[-1].weighted_rms prev_rms = self.iters[-2].weighted_rms del_rms = abs(prev_rms - curr_rms)#/prev_rms From 2e68b78b2832990d0a5d7e59640e41b962646255 Mon Sep 17 00:00:00 2001 From: Rahil Makadia Date: Wed, 21 Aug 2024 19:57:43 -0500 Subject: [PATCH 11/12] b-plane partial upgrades --- grss/prop/prop_utils.py | 56 ++++----- grss/version.txt | 2 +- include/simulation.h | 6 +- include/utilities.h | 6 + src/approach.cpp | 246 ++++++++++++++++++++++++++++------------ src/grss.cpp | 10 +- src/utilities.cpp | 16 +++ 7 files changed, 222 insertions(+), 120 deletions(-) diff --git a/grss/prop/prop_utils.py b/grss/prop/prop_utils.py index 8ebaed51..68d61fe5 100644 --- a/grss/prop/prop_utils.py +++ b/grss/prop/prop_utils.py @@ -327,8 +327,7 @@ def partials_to_ellipse(ca, au2units, n_std, print_ellipse_params, units, analyt units : str units of the plot analytic_info : tuple - Tuple of analytic B-plane information (initial covariance - and state conversion partial derivatives) + Tuple of analytic B-plane information (just initial covariance for now) Returns ------- @@ -341,48 +340,31 @@ def partials_to_ellipse(ca, au2units, n_std, print_ellipse_params, units, analyt init_cov = np.array(analytic_info[0]) else: init_cov = analytic_info[0] - if not isinstance(analytic_info[1], np.ndarray): - part_cart_part_state = np.array(analytic_info[1]) - else: - part_cart_part_state = analytic_info[1] - part_state_part_cart = np.linalg.inv(part_cart_part_state) - stm_flat = ca.xRel[6:] - stm_tca_t0 = np.array(libgrss.reconstruct_stm(stm_flat)) @ part_state_part_cart stm_flat = ca.xRelMap[6:] - stm_tmap_t0 = np.array(libgrss.reconstruct_stm(stm_flat)) @ part_state_part_cart - if ca.t == ca.tMap: - stm_tca_tmap = np.eye(stm_tca_t0.shape[0]) - else: - stm_tca_tmap = stm_tca_t0 @ np.linalg.inv(stm_tmap_t0) - - extra_zeros = [0]*(stm_tca_tmap.shape[0]-6) - part_tlin_minus_t = ca.dTLinMinusT + extra_zeros - part_tlin = part_tlin_minus_t @ stm_tca_tmap - part_t = ca.dt + extra_zeros - part_tlin += part_t - part_kizner = np.zeros((3,stm_tca_tmap.shape[0])) - part_kizner[:2,:] = np.array([ca.kizner.dx+extra_zeros,ca.kizner.dy+extra_zeros])@stm_tca_tmap - part_kizner[2,:] = part_tlin - part_scaled = np.zeros((3,stm_tca_tmap.shape[0])) - part_scaled[:2,:] = np.array([ca.scaled.dx+extra_zeros,ca.scaled.dy+extra_zeros])@stm_tca_tmap - part_scaled[2,:] = part_tlin - part_opik = np.zeros((3,stm_tca_tmap.shape[0])) - part_opik[:2,:] = np.array([ca.opik.dx+extra_zeros,ca.opik.dy+extra_zeros])@stm_tca_tmap - part_opik[:2,:6] += ca.dOpikTotalDerivTerm2 - part_opik[2,:] = part_tlin - part_mtp = np.zeros((3,stm_tca_tmap.shape[0])) - part_mtp[:2,:] = np.array([ca.mtp.dx+extra_zeros,ca.mtp.dy+extra_zeros])@stm_tca_tmap - part_mtp[2,:] = part_tlin - - init_cart_cov = part_cart_part_state @ init_cov @ part_cart_part_state.T - cov_tmap = stm_tmap_t0 @ init_cart_cov @ stm_tmap_t0.T + stm_tmap_t0 = np.array(libgrss.reconstruct_stm(stm_flat))# @ part_state_part_cart + + extra_zeros = [0]*(stm_tmap_t0.shape[0]-6) + part_kizner = np.zeros((3,stm_tmap_t0.shape[0])) + part_kizner[:2,:] = np.array([ca.kizner.dx+extra_zeros,ca.kizner.dy+extra_zeros]) + part_kizner[2,:] = ca.dtLin + extra_zeros + part_scaled = np.zeros((3,stm_tmap_t0.shape[0])) + part_scaled[:2,:] = np.array([ca.scaled.dx+extra_zeros,ca.scaled.dy+extra_zeros]) + part_scaled[2,:] = ca.dtLin + extra_zeros + part_opik = np.zeros((3,stm_tmap_t0.shape[0])) + part_opik[:2,:] = np.array([ca.opik.dx+extra_zeros,ca.opik.dy+extra_zeros]) + part_opik[2,:] = ca.dtLin + extra_zeros + part_mtp = np.zeros((3,stm_tmap_t0.shape[0])) + part_mtp[:2,:] = np.array([ca.mtp.dx+extra_zeros,ca.mtp.dy+extra_zeros]) + part_mtp[2,:] = ca.dt + extra_zeros + + cov_tmap = stm_tmap_t0 @ init_cov @ stm_tmap_t0.T cov_kizner = part_kizner @ cov_tmap @ part_kizner.T cov_scaled = part_scaled @ cov_tmap @ part_scaled.T cov_opik = part_opik @ cov_tmap @ part_opik.T cov_mtp = part_mtp @ cov_tmap @ part_mtp.T - t_dev = (part_t @ cov_tmap @ part_t)**0.5 + t_dev = cov_mtp[2,2]**0.5 cov_kizner = cov_kizner[:2,:2]*au2units**2 cov_opik = cov_opik[:2,:2]*au2units**2 diff --git a/grss/version.txt b/grss/version.txt index b673f6ac..ef8d7569 100644 --- a/grss/version.txt +++ b/grss/version.txt @@ -1 +1 @@ -4.1.5 \ No newline at end of file +4.2.0 \ No newline at end of file diff --git a/include/simulation.h b/include/simulation.h index 270004e5..472f6eb1 100644 --- a/include/simulation.h +++ b/include/simulation.h @@ -296,9 +296,8 @@ struct BPlaneParameters { * @param opik Öpik B-plane parameters. * @param scaled Scaled Kizner B-plane parameters. * @param mtp Modified Target Plane parameters. - * @param dTLinMinusT Partial derivatives of the (linearized intersection minus map) time with respect to the state at the close approach. + * @param dtLin Partial derivatives of the (linearized intersection minus map) time with respect to the state at the close approach. * @param dt Partial derivatives of the time of closest approach with respect to the state at the close approach. - * @param dOpikTotalDerivTerm2 Partial derivatives of the Öpik total derivative term 2 (non-constant planet velocity). */ class CloseApproachParameters { private: @@ -327,9 +326,8 @@ class CloseApproachParameters { BPlaneParameters opik; BPlaneParameters scaled; BPlaneParameters mtp; - std::vector dTLinMinusT = std::vector(6, 0.0L); + std::vector dtLin = std::vector(6, 0.0L); std::vector dt = std::vector(6, 0.0L); - std::vector< std::vector > dOpikTotalDerivTerm2 = std::vector< std::vector >(2, std::vector(6, 0.0L)); /** * @brief Get the close approach parameters. */ diff --git a/include/utilities.h b/include/utilities.h index 815caf0c..2228f6d7 100644 --- a/include/utilities.h +++ b/include/utilities.h @@ -181,6 +181,12 @@ void vabs_max(const real *v, const size_t &dim, real &max); void mat_vec_mul(const std::vector> &A, const std::vector &v, std::vector &Av); +/** + * @brief Multiply a vector by a matrix. + */ +void vec_mat_mul(const std::vector &v, real **A, + const size_t &dim, std::vector &vA); + /** * @brief Multiply two matrices. */ diff --git a/src/approach.cpp b/src/approach.cpp index 0d4b2633..c419c327 100644 --- a/src/approach.cpp +++ b/src/approach.cpp @@ -394,8 +394,8 @@ void CloseApproachParameters::get_ca_parameters(PropSimulation *propSim, const r const real v = sqrt(xRelMap[3] * xRelMap[3] + xRelMap[4] * xRelMap[4] + xRelMap[5] * xRelMap[5]); this->vel = v; - const real a = (mu * r) / (2 * mu - r * v * v); - const real vInf = sqrt(-mu / a); + real a = (mu * r) / (2 * mu - r * v * v); + real vInf = sqrt(-mu / a); this->vInf = vInf; real h, pos[3], vel[3], hVec[3]; for (size_t k = 0; k < 3; k++) { @@ -454,10 +454,10 @@ void CloseApproachParameters::get_ca_parameters(PropSimulation *propSim, const r this->scaled.z = this->kizner.z / this->gravFocusFactor; real posDotVel; vdot(pos, vel, 3, posDotVel); - const real sinhF = vInf*posDotVel/mu/e; - const real F = asinh(sinhF); // or F = -log(2*r/a/e); - const real n = sqrt(-mu / a / a / a); - this->tPeri = tMap - (e*sinhF - F)/n; + real sinhF = vInf*posDotVel/mu/e; + real F = asinh(sinhF); // or F = -log(2*r/a/e); + real n = sqrt(-mu / a / a / a); + this->tPeri = tMap + (F - e*sinhF)/n; this->tLin = this->tPeri - log(e) / n; // calculate B-plane parameters (Öpik xi, zeta formulation) double xCentralBody[9]; @@ -494,7 +494,7 @@ void CloseApproachParameters::get_ca_parameters(PropSimulation *propSim, const r vdot(posCA, eHatY, 3, this->mtp.y); vdot(posCA, eHatZ, 3, this->mtp.z); - if (tMap == this->tCA && propSim->integBodies[i].propStm == true){ + if (propSim->integBodies[i].propStm == true){ real **partial_r_vec = new real*[6]; real **partial_v_vec = new real*[6]; for (size_t k = 0; k < 6; k++) { @@ -514,7 +514,6 @@ void CloseApproachParameters::get_ca_parameters(PropSimulation *propSim, const r partial_v_vec[4][1] = 1; partial_v_vec[5][2] = 1; - real alpha = vInf*vInf; real *partial_alpha = new real[6]; real *partial_vInf = new real[6]; real *partial_a = new real[6]; @@ -523,15 +522,22 @@ void CloseApproachParameters::get_ca_parameters(PropSimulation *propSim, const r vnorm(posCA, 3, rCA); vnorm(velCA, 3, vCA); rCA3 = rCA*rCA*rCA; + a = (mu * rCA) / (2 * mu - rCA * vCA * vCA); + n = sqrt(-mu / a / a / a); + vInf = sqrt(-mu / a); for (size_t k = 0; k < 6; k++) { vdot(velCA, partial_v_vec[k], 3, temp1); vdot(posCA, partial_r_vec[k], 3, temp2); partial_alpha[k] = 2*(temp1 + mu*temp2/rCA3); - partial_vInf[k] = partial_alpha[k]/(2*vInf); - partial_a[k] = -a*partial_alpha[k]/alpha; + partial_vInf[k] = partial_alpha[k]/2/vInf; + partial_a[k] = -a*partial_alpha[k]/(vCA*vCA - 2*mu/rCA); partial_n[k] = -3*n*partial_a[k]/2/a; } + real hVecCA[3]; + vcross(posCA, velCA, hVecCA); + real hCA; + vnorm(hVecCA, 3, hCA); real **partial_hVec = new real*[6]; real *partial_h = new real[6]; for (size_t k = 0; k < 6; k++) { @@ -544,17 +550,21 @@ void CloseApproachParameters::get_ca_parameters(PropSimulation *propSim, const r for (size_t k2 = 0; k2 < 3; k2++) { partial_hVec[k][k2] += temp1Vec[k2]; } - vdot(partial_hVec[k], hVec, 3, partial_h[k]); - partial_h[k] /= h; + vdot(partial_hVec[k], hVecCA, 3, partial_h[k]); + partial_h[k] /= hCA; } + real vCrossHCA[3], eVecCA[3]; + vcross(velCA, hVecCA, vCrossHCA); + for (size_t k = 0; k < 3; k++) { + eVecCA[k] = vCrossHCA[k] / mu - posCA[k] / rCA; + } + vnorm(eVecCA, 3, e); real **partial_eVec = new real*[6]; real *partial_e = new real[6]; for (size_t k = 0; k < 6; k++) { partial_eVec[k] = new real[3]; - } - for (size_t k = 0; k < 6; k++) { - vcross(partial_v_vec[k], hVec, temp1Vec); + vcross(partial_v_vec[k], hVecCA, temp1Vec); for (size_t k2 = 0; k2 < 3; k2++) { partial_eVec[k][k2] = temp1Vec[k2] / mu; } @@ -563,25 +573,31 @@ void CloseApproachParameters::get_ca_parameters(PropSimulation *propSim, const r partial_eVec[k][k2] += temp1Vec[k2] / mu; } for (size_t k2 = 0; k2 < 3; k2++) { - partial_eVec[k][k2] -= partial_r_vec[k][k2] / r; + partial_eVec[k][k2] -= partial_r_vec[k][k2] / rCA; } vdot(posCA, partial_r_vec[k], 3, temp1Vec[0]); for (size_t k2 = 0; k2 < 3; k2++) { - partial_eVec[k][k2] += temp1Vec[0] * posCA[k2] / (r * r * r); + partial_eVec[k][k2] += temp1Vec[0] * posCA[k2] / (rCA * rCA * rCA); } vdot(partial_eVec[k], eVec, 3, partial_e[k]); partial_e[k] /= e; } real *partial_F = new real[6]; + real posDotVelCA; + vdot(posCA, velCA, 3, posDotVelCA); + sinhF = vInf*posDotVelCA/mu/e; + F = asinh(sinhF); // or F = -log(2*r/a/e); for (size_t k = 0; k < 6; k++) { vdot(posCA, partial_v_vec[k], 3, temp1); vdot(velCA, partial_r_vec[k], 3, temp2); - partial_F[k] = (posDotVel*partial_vInf[k]/mu + vInf*(temp1+temp2)/mu - partial_e[k]*sinhF)/e/cosh(F); + partial_F[k] = (posDotVelCA*partial_vInf[k]/mu + vInf*(temp1+temp2)/mu - partial_e[k]*sinhF)/e/cosh(F); } - std::vector accCA(propSim->integParams.n2Derivs, 0.0); - get_state_der(propSim, tMap, this->xRelCA, accCA); - real accRel[3], accPlanet[3]; + + std::vector accMap(propSim->integParams.n2Derivs, 0.0); + std::vector xMap = propSim->interpolate(this->tMap); + get_state_der(propSim, tMap, xMap, accMap); + real accRelMap[3], accPlanet[3]; if (j < propSim->integParams.nInteg) { accPlanet[0] = propSim->integBodies[j].acc[0]; accPlanet[1] = propSim->integBodies[j].acc[1]; @@ -592,17 +608,31 @@ void CloseApproachParameters::get_ca_parameters(PropSimulation *propSim, const r accPlanet[2] = propSim->spiceBodies[j - propSim->integParams.nInteg].acc[2]; } for (size_t k = 0; k < 3; k++) { - accRel[k] = propSim->integBodies[i].acc[k] - accPlanet[k]; + accRelMap[k] = propSim->integBodies[i].acc[k] - accPlanet[k]; } - real posDotAcc, posCADotVelCA; - vdot(posCA, accRel, 3, posDotAcc); - vdot(posCA, velCA, 3, posCADotVelCA); + real posDotAccMap; + vdot(pos, accRelMap, 3, posDotAccMap); + std::vector dtLinMinustCA(6, 0.0); for (size_t k = 0; k < 6; k++) { - this->dTLinMinusT[k] = (r*partial_F[k]/a - (e*sinhF + 1)*partial_e[k]/e - (tLin-tMap)*partial_n[k])/n; - vdot(posCA, partial_v_vec[k], 3, temp1); - vdot(velCA, partial_r_vec[k], 3, temp2); - this->dt[k] = -(temp1+temp2)/(posDotAcc + vCA*vCA); + dtLinMinustCA[k] = (rCA*partial_F[k]/a - (e*sinhF + 1)*partial_e[k]/e - (tLin-this->tCA)*partial_n[k])/n; + vdot(pos, partial_v_vec[k], 3, temp1); + vdot(vel, partial_r_vec[k], 3, temp2); + this->dt[k] = -(temp1+temp2)/(posDotAccMap + v*v); + } + + real **partial_xCA = new real*[6]; + for (size_t k = 0; k < 6; k++) { + partial_xCA[k] = new real[6]; + for (size_t k2 = 0; k2 < 6; k2++) { + partial_xCA[k][k2] = 0; + } + } + for (size_t k = 0; k < 3; k++) { + for (size_t k2 = 0; k2 < 6; k2++) { + partial_xCA[k][k2] = partial_r_vec[k2][k] + vel[k]*this->dt[k2]; + partial_xCA[k+3][k2] = partial_v_vec[k2][k] + accRelMap[k]*this->dt[k2]; + } } real **partial_pHat = new real*[6]; @@ -621,12 +651,15 @@ void CloseApproachParameters::get_ca_parameters(PropSimulation *propSim, const r partial_pHat[k][k2] = partial_eVec[k][k2]/e - partial_e[k]*eVec[k2]/e/e; } vcross(partial_hVec[k], pHat, temp1Vec); - vcross(hVec, partial_pHat[k], temp2Vec); + vcross(hVecCA, partial_pHat[k], temp2Vec); for (size_t k2 = 0; k2 < 3; k2++) { - partial_qHat[k][k2] = (temp1Vec[k2] + temp2Vec[k2] - partial_h[k]*qHat[k2])/h; + partial_qHat[k][k2] = (temp1Vec[k2] + temp2Vec[k2] - partial_h[k]*qHat[k2])/hCA; } for (size_t k2 = 0; k2 < 3; k2++) { - partial_sHat[k][k2] = -partial_e[k]*pHat[k2]/e/e + partial_pHat[k][k2]/e + partial_e[k]*qHat[k2]/e/e/sqrt(e*e-1) + sqrt(e*e-1)*partial_qHat[k][k2]/e; + partial_sHat[k][k2] = -partial_e[k] * pHat[k2] / e / e + + partial_pHat[k][k2] / e + + partial_e[k] * qHat[k2] / e / e / sqrt(e * e - 1) + + sqrt(e * e - 1) * partial_qHat[k][k2] / e; } vcross(vVec, partial_sHat[k], temp1Vec); vdot(tHat, temp1Vec, 3, temp1); @@ -641,21 +674,37 @@ void CloseApproachParameters::get_ca_parameters(PropSimulation *propSim, const r partial_rHat[k][k2] = temp1Vec[k2] + temp2Vec[k2]; } } + + real lambda = sqrt(1.0 + 2 * mu / radius / vInf / vInf); real *partial_lambda = new real[6]; + std::vector k_dx(6, 0.0); + std::vector k_dy(6, 0.0); + std::vector s_dx(6, 0.0); + std::vector s_dy(6, 0.0); for (size_t k = 0; k < 6; k++) { vdot(tHat, partial_hVec[k], 3, temp1); - vdot(hVec, partial_tHat[k], 3, temp2); - this->kizner.dx[k] = (temp1 + temp2 - this->kizner.x*partial_vInf[k])/vInf; + vdot(hVecCA, partial_tHat[k], 3, temp2); + k_dx[k] = (temp1 + temp2 - this->kizner.x*partial_vInf[k])/vInf; vdot(rHat, partial_hVec[k], 3, temp1); - vdot(hVec, partial_rHat[k], 3, temp2); + vdot(hVecCA, partial_rHat[k], 3, temp2); + k_dy[k] = -(temp1 + temp2 + this->kizner.y*partial_vInf[k])/vInf; + + partial_lambda[k] = -2*mu*partial_vInf[k]/lambda/radius/vInf/vInf/vInf; + s_dx[k] = (k_dx[k] - this->scaled.x*partial_lambda[k])/lambda; + s_dy[k] = (k_dy[k] - this->scaled.y*partial_lambda[k])/lambda; + } - partial_lambda[k] = -2*mu*partial_vInf[k]/this->gravFocusFactor/radius/vInf/vInf/vInf; - this->kizner.dy[k] = -(temp1 + temp2 + this->kizner.y*partial_vInf[k])/vInf; - this->scaled.dx[k] = (this->kizner.dx[k] - this->scaled.x*partial_lambda[k])/this->gravFocusFactor; - this->scaled.dy[k] = (this->kizner.dy[k] - this->scaled.y*partial_lambda[k])/this->gravFocusFactor; + // map off-nominal trajectory for full partials + vec_mat_mul(k_dx, partial_xCA, 6, this->kizner.dx); + vec_mat_mul(k_dy, partial_xCA, 6, this->kizner.dy); + vec_mat_mul(s_dx, partial_xCA, 6, this->scaled.dx); + vec_mat_mul(s_dy, partial_xCA, 6, this->scaled.dy); + vec_mat_mul(dtLinMinustCA, partial_xCA, 6, this->dtLin); + for (size_t k = 0; k < 6; k++) { + this->dtLin[k] += this->dt[k]; } - // Acceleration of the Sun is not included in the calculation + // Acceleration of the Sun for opik formulation real accSun[3]; for (size_t k = 0; k < propSim->integParams.nSpice; k++) { if (propSim->spiceBodies[k].spiceId == 10) { @@ -665,11 +714,11 @@ void CloseApproachParameters::get_ca_parameters(PropSimulation *propSim, const r break; } } - real **partial_vel_planet = new real*[3]; - for (size_t k = 0; k < 3; k++) { - partial_vel_planet[k] = new real[6]; - for (size_t k2 = 0; k2 < 6; k2++) { - partial_vel_planet[k][k2] = this->dt[k2]*(accPlanet[k] - accSun[k]); + real **partial_vel_planet = new real*[6]; + for (size_t k = 0; k < 6; k++) { + partial_vel_planet[k] = new real[3]; + for (size_t k2 = 0; k2 < 3; k2++) { + partial_vel_planet[k][k2] = this->dt[k]*(accPlanet[k2] - accSun[k2]); } } // partials of xi and zeta w.r.t planet velocity are needed for total derivative @@ -679,8 +728,10 @@ void CloseApproachParameters::get_ca_parameters(PropSimulation *propSim, const r for (size_t k2 = 0; k2 < 3; k2++) { partial_vpl_vpl[k][k2] = 0; } - partial_vpl_vpl[k][k] = 1; } + partial_vpl_vpl[0][0] = 1; + partial_vpl_vpl[1][1] = 1; + partial_vpl_vpl[2][2] = 1; real vPlanetCrossSHat; vnorm(vPlanetCrossSHatVec, 3, vPlanetCrossSHat); real **partial_vPlanetCrossSHatVec_vpl = new real*[3]; @@ -712,34 +763,27 @@ void CloseApproachParameters::get_ca_parameters(PropSimulation *propSim, const r real *partial_xi_vpl = new real[3]; real *partial_zeta_vpl = new real[3]; for (size_t k = 0; k < 3; k++) { - vdot(partial_zetaHat_vpl[k], hVec, 3, partial_xi_vpl[k]); + vdot(partial_zetaHat_vpl[k], hVecCA, 3, partial_xi_vpl[k]); partial_xi_vpl[k] /= vInf; - vdot(partial_xiHat_vpl[k], hVec, 3, partial_zeta_vpl[k]); + vdot(partial_xiHat_vpl[k], hVecCA, 3, partial_zeta_vpl[k]); partial_zeta_vpl[k] /= -vInf; } + std::vector> opikTotalDerivTerm2(2, std::vector(6,0.0)); for (size_t k = 0; k < 6; k++) { - for (size_t k2 = 0; k2 < 3; k2++) { - temp1Vec[k2] = partial_vel_planet[k2][k]; - } - vdot(partial_xi_vpl, temp1Vec, 3, this->dOpikTotalDerivTerm2[0][k]); - vdot(partial_zeta_vpl, temp1Vec, 3, this->dOpikTotalDerivTerm2[1][k]); + vdot(partial_xi_vpl, partial_vel_planet[k], 3, opikTotalDerivTerm2[0][k]); + vdot(partial_zeta_vpl, partial_vel_planet[k], 3, opikTotalDerivTerm2[1][k]); } real **partial_xiHat = new real*[6]; - real temp3, temp3Vec[3]; - real temp4, temp4Vec[3]; for (size_t k = 0; k < 6; k++) { partial_xiHat[k] = new real[3]; - vcross(partial_v_vec[k], sHat, temp1Vec); + vcross(partial_vel_planet[k], sHat, temp1Vec); vcross(vCentralBodyHelio, partial_sHat[k], temp2Vec); - vcross(vCentralBodyHelio, sHat, temp3Vec); - vnorm(temp3Vec, 3, temp3); + temp1 = xiHat[0] * (temp1Vec[0] + temp2Vec[0]) + + xiHat[1] * (temp1Vec[1] + temp2Vec[1]) + + xiHat[2] * (temp1Vec[2] + temp2Vec[2]); for (size_t k2 = 0; k2 < 3; k2++) { - temp4Vec[0] = (temp1Vec[k2]+temp2Vec[k2])*xiHat[0]; - temp4Vec[1] = (temp1Vec[k2]+temp2Vec[k2])*xiHat[1]; - temp4Vec[2] = (temp1Vec[k2]+temp2Vec[k2])*xiHat[2]; - vdot(xiHat, temp4Vec, 3, temp4); - partial_xiHat[k][k2] = (temp1Vec[k2] + temp2Vec[k2] - temp4)/temp3; + partial_xiHat[k][k2] = (temp1Vec[k2] + temp2Vec[k2] - temp1*xiHat[k2])/vPlanetCrossSHat; } } real **partial_zetaHat = new real*[6]; @@ -751,17 +795,73 @@ void CloseApproachParameters::get_ca_parameters(PropSimulation *propSim, const r partial_zetaHat[k][k2] = -(temp1Vec[k2] + temp2Vec[k2]); } } + std::vector o_dx(6, 0.0); + std::vector o_dy(6, 0.0); for (size_t k = 0; k < 6; k++) { vdot(zetaHat, partial_hVec[k], 3, temp1); - vdot(hVec, partial_zetaHat[k], 3, temp2); - this->opik.dx[k] = (temp1 + temp2 - this->opik.x*partial_vInf[k])/vInf; + vdot(hVecCA, partial_zetaHat[k], 3, temp2); + o_dx[k] = (temp1 + temp2 - this->opik.x*partial_vInf[k])/vInf; vdot(xiHat, partial_hVec[k], 3, temp1); - vdot(hVec, partial_xiHat[k], 3, temp2); - this->opik.dy[k] = -(temp1 + temp2 + this->opik.y*partial_vInf[k])/vInf; + vdot(hVecCA, partial_xiHat[k], 3, temp2); + o_dy[k] = -(temp1 + temp2 + this->opik.y*partial_vInf[k])/vInf; + } + // map off-nominal trajectory for full partials + vec_mat_mul(o_dx, partial_xCA, 6, this->opik.dx); + vec_mat_mul(o_dy, partial_xCA, 6, this->opik.dy); + for (size_t k = 0; k < 6; k++) { + this->opik.dx[k] += opikTotalDerivTerm2[0][k]; + this->opik.dy[k] += opikTotalDerivTerm2[1][k]; + } + + // calculate mtp derivatives + real **partial_eHatZ = new real*[6]; + for (size_t k = 0; k < 6; k++) { + partial_eHatZ[k] = new real[3]; + for (size_t k2 = 0; k2 < 3; k2++) { + temp1Vec[k2] = partial_xCA[k2+3][k]; + partial_eHatZ[k][k2] = 0; + } + vdot(eHatZ, temp1Vec, 3, temp1); + for (size_t k2 = 0; k2 < 3; k2++) { + partial_eHatZ[k][k2] = (temp1Vec[k2] - temp1*eHatZ[k2])/vCA; + } + } + real **partial_eHatY = new real*[6]; + vnorm(vVecCrosseHatZ, 3, temp2); + for (size_t k = 0; k < 6; k++) { + partial_eHatY[k] = new real[3]; + for (size_t k2 = 0; k2 < 3; k2++) { + vcross(vVec, partial_eHatZ[k], temp1Vec); + vdot(eHatY, temp1Vec, 3, temp1); + partial_eHatY[k][k2] = (temp1Vec[k2] - temp1*eHatY[k2])/temp2; + } + } + real **partial_eHatX = new real*[6]; + for (size_t k = 0; k < 6; k++) { + partial_eHatX[k] = new real[3]; + vcross(partial_eHatY[k], eHatZ, temp1Vec); + vcross(eHatY, partial_eHatZ[k], temp2Vec); + for (size_t k2 = 0; k2 < 3; k2++) { + partial_eHatX[k][k2] = temp1Vec[k2] + temp2Vec[k2]; + } } + std::vector m_dx(6, 0.0); + std::vector m_dy(6, 0.0); + for (size_t k = 0; k < 6; k++) { + for (size_t k2 = 0; k2 < 3; k2++) { + temp1Vec[k2] = partial_xCA[k2][k]; + } + vdot(temp1Vec, eHatX, 3, temp1); + vdot(posCA, partial_eHatX[k], 3, temp2); + m_dx[k] = temp1 + temp2; + vdot(temp1Vec, eHatY, 3, temp1); + vdot(posCA, partial_eHatY[k], 3, temp2); + m_dy[k] = temp1 + temp2; + } + // map off-nominal trajectory for full partials + vec_mat_mul(m_dx, partial_xCA, 6, this->mtp.dx); + vec_mat_mul(m_dy, partial_xCA, 6, this->mtp.dy); - this->mtp.dx = {eHatX[0], eHatX[1], eHatX[2], 0.0, 0.0, 0.0}; - this->mtp.dy = {eHatY[0], eHatY[1], eHatY[2], 0.0, 0.0, 0.0}; // clean up for (size_t k = 0; k < 6; k++) { delete[] partial_r_vec[k]; @@ -773,11 +873,14 @@ void CloseApproachParameters::get_ca_parameters(PropSimulation *propSim, const r delete[] partial_rHat[k]; delete[] partial_sHat[k]; delete[] partial_tHat[k]; + delete[] partial_vel_planet[k]; delete[] partial_xiHat[k]; delete[] partial_zetaHat[k]; + delete[] partial_eHatZ[k]; + delete[] partial_eHatY[k]; + delete[] partial_eHatX[k]; } for (size_t k = 0; k < 3; k++) { - delete[] partial_vel_planet[k]; delete[] partial_vpl_vpl[k]; delete[] partial_vPlanetCrossSHatVec_vpl[k]; delete[] partial_xiHat_vpl[k]; @@ -807,6 +910,9 @@ void CloseApproachParameters::get_ca_parameters(PropSimulation *propSim, const r delete[] partial_zeta_vpl; delete[] partial_xiHat; delete[] partial_zetaHat; + delete[] partial_eHatZ; + delete[] partial_eHatY; + delete[] partial_eHatX; } } diff --git a/src/grss.cpp b/src/grss.cpp index e4637960..b1f54fa9 100644 --- a/src/grss.cpp +++ b/src/grss.cpp @@ -246,18 +246,12 @@ PYBIND11_MODULE(libgrss, m) { .def_readwrite("mtp", &CloseApproachParameters::mtp, R"mydelimiter( Modified Target Plane (MTP) B-plane parameters of the close approach. )mydelimiter") - .def_readwrite("dTLinMinusT", &CloseApproachParameters::dTLinMinusT, - R"mydelimiter( - Partials of difference between linearized time of periapsis and time of periapsis with respect to CA state. + .def_readwrite("dtLin", &CloseApproachParameters::dtLin, R"mydelimiter( + Partials of linearized time of periapsis with respect to CA state. )mydelimiter") .def_readwrite("dt", &CloseApproachParameters::dt, R"mydelimiter( Partials of time of periapsis with respect to CA state. )mydelimiter") - .def_readwrite("dOpikTotalDerivTerm2", - &CloseApproachParameters::dOpikTotalDerivTerm2, - R"mydelimiter( - Partial derivatives of the Öpik total derivative term 2 (non-constant planet velocity). - )mydelimiter") .def("get_ca_parameters", &CloseApproachParameters::get_ca_parameters, py::arg("propSim"), py::arg("tMap"), R"mydelimiter( Calculate the close approach parameters. diff --git a/src/utilities.cpp b/src/utilities.cpp index 6a75b494..a4b750b8 100644 --- a/src/utilities.cpp +++ b/src/utilities.cpp @@ -260,6 +260,22 @@ void mat_vec_mul(const std::vector> &A, } } +/** + * @param[in] v Vector. + * @param[in] A Matrix. + * @param[in] dim Dimension of the vector and matrix. + * @param[out] vA Product of v and A. + */ +void vec_mat_mul(const std::vector &v, real **A, + const size_t &dim, std::vector &vA) { + for (size_t i = 0; i < dim; i++) { + vA[i] = 0; + for (size_t j = 0; j < dim; j++) { + vA[i] += v[j] * A[j][i]; + } + } +} + /** * @param[in] A Matrix 1. * @param[in] B Matrix 2. From 8aa3b511eb2323a8481b2e93dab6c9bb8513110a Mon Sep 17 00:00:00 2001 From: Rahil Makadia Date: Thu, 22 Aug 2024 22:51:05 -0500 Subject: [PATCH 12/12] B-plane partials finalized --- grss/fit/fit_simulation.py | 2 +- src/approach.cpp | 15 ++++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/grss/fit/fit_simulation.py b/grss/fit/fit_simulation.py index 0ca6ba4a..f22b5243 100644 --- a/grss/fit/fit_simulation.py +++ b/grss/fit/fit_simulation.py @@ -1797,7 +1797,7 @@ def print_summary(self, iter_idx=-1, out_stream=sys.stdout): str_to_print += f"{key}\t\t\t{init_val:.11e}\t\t{init_unc:.11e}" str_to_print += f"\t\t{final_val:.11e}\t\t{final_unc:.11e}" str_to_print += f"\t\t{final_val-init_val:+.11e}" - str_to_print += f"\t\t{(final_val-init_val)/init_unc:+.3f}\n" + str_to_print += f"\t\t{(final_val-init_val)/final_unc:+.3f}\n" print(str_to_print, file=out_stream) return None diff --git a/src/approach.cpp b/src/approach.cpp index c419c327..d34a6242 100644 --- a/src/approach.cpp +++ b/src/approach.cpp @@ -777,13 +777,18 @@ void CloseApproachParameters::get_ca_parameters(PropSimulation *propSim, const r real **partial_xiHat = new real*[6]; for (size_t k = 0; k < 6; k++) { partial_xiHat[k] = new real[3]; - vcross(partial_vel_planet[k], sHat, temp1Vec); + /* the variation from changing planet velocity comes from + opikTotalDerivTerm2, so we dont need the below cross product */ + // vcross(partial_vel_planet[k], sHat, temp1Vec); vcross(vCentralBodyHelio, partial_sHat[k], temp2Vec); - temp1 = xiHat[0] * (temp1Vec[0] + temp2Vec[0]) + - xiHat[1] * (temp1Vec[1] + temp2Vec[1]) + - xiHat[2] * (temp1Vec[2] + temp2Vec[2]); + // temp1 = xiHat[0] * (temp1Vec[0] + temp2Vec[0]) + + // xiHat[1] * (temp1Vec[1] + temp2Vec[1]) + + // xiHat[2] * (temp1Vec[2] + temp2Vec[2]); + temp1 = xiHat[0] * temp2Vec[0] + xiHat[1] * temp2Vec[1] + + xiHat[2] * temp2Vec[2]; for (size_t k2 = 0; k2 < 3; k2++) { - partial_xiHat[k][k2] = (temp1Vec[k2] + temp2Vec[k2] - temp1*xiHat[k2])/vPlanetCrossSHat; + // partial_xiHat[k][k2] = (temp1Vec[k2] + temp2Vec[k2] - temp1*xiHat[k2])/vPlanetCrossSHat; + partial_xiHat[k][k2] = (temp2Vec[k2] - temp1*xiHat[k2])/vPlanetCrossSHat; } } real **partial_zetaHat = new real*[6];