grss 4.2.0__tar.gz → 4.3.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {grss-4.2.0/grss.egg-info → grss-4.3.2}/PKG-INFO +1 -1
- {grss-4.2.0 → grss-4.3.2}/grss/fit/fit_ades.py +1 -0
- {grss-4.2.0 → grss-4.3.2}/grss/fit/fit_optical.py +2 -1
- {grss-4.2.0 → grss-4.3.2}/grss/fit/fit_radar.py +0 -9
- {grss-4.2.0 → grss-4.3.2}/grss/fit/fit_simulation.py +241 -74
- {grss-4.2.0 → grss-4.3.2}/grss/kernels/get_kernels.py +10 -10
- {grss-4.2.0 → grss-4.3.2}/grss/utils.py +13 -0
- grss-4.3.2/grss/version.txt +1 -0
- {grss-4.2.0 → grss-4.3.2/grss.egg-info}/PKG-INFO +1 -1
- {grss-4.2.0 → grss-4.3.2}/include/gr15.h +18 -6
- {grss-4.2.0 → grss-4.3.2}/include/grss.h +2 -1
- {grss-4.2.0 → grss-4.3.2}/include/simulation.h +44 -22
- {grss-4.2.0 → grss-4.3.2}/include/stm.h +6 -0
- {grss-4.2.0 → grss-4.3.2}/src/force.cpp +41 -0
- {grss-4.2.0 → grss-4.3.2}/src/gr15.cpp +102 -42
- {grss-4.2.0 → grss-4.3.2}/src/grss.cpp +64 -53
- {grss-4.2.0 → grss-4.3.2}/src/interpolate.cpp +18 -26
- {grss-4.2.0 → grss-4.3.2}/src/observe.cpp +101 -66
- {grss-4.2.0 → grss-4.3.2}/src/pck.cpp +3 -2
- {grss-4.2.0 → grss-4.3.2}/src/simulation.cpp +173 -43
- {grss-4.2.0 → grss-4.3.2}/src/stm.cpp +51 -0
- {grss-4.2.0 → grss-4.3.2}/tests/cpp/prop/pck_map.cpp +5 -5
- grss-4.2.0/grss/version.txt +0 -1
- {grss-4.2.0 → grss-4.3.2}/CMakeLists.txt +0 -0
- {grss-4.2.0 → grss-4.3.2}/LICENSE +0 -0
- {grss-4.2.0 → grss-4.3.2}/MANIFEST.in +0 -0
- {grss-4.2.0 → grss-4.3.2}/README.md +0 -0
- {grss-4.2.0 → grss-4.3.2}/build_cpp.sh +0 -0
- {grss-4.2.0 → grss-4.3.2}/docs/source/conf.py +0 -0
- {grss-4.2.0 → grss-4.3.2}/extern/get_cspice.py +0 -0
- {grss-4.2.0 → grss-4.3.2}/grss/__init__.py +0 -0
- {grss-4.2.0 → grss-4.3.2}/grss/debias/get_debiasing_data.py +0 -0
- {grss-4.2.0 → grss-4.3.2}/grss/fit/__init__.py +0 -0
- {grss-4.2.0 → grss-4.3.2}/grss/fit/fit_utils.py +0 -0
- {grss-4.2.0 → grss-4.3.2}/grss/prop/__init__.py +0 -0
- {grss-4.2.0 → grss-4.3.2}/grss/prop/prop_parallel.py +0 -0
- {grss-4.2.0 → grss-4.3.2}/grss/prop/prop_unscented.py +0 -0
- {grss-4.2.0 → grss-4.3.2}/grss/prop/prop_utils.py +0 -0
- {grss-4.2.0 → grss-4.3.2}/grss.egg-info/SOURCES.txt +0 -0
- {grss-4.2.0 → grss-4.3.2}/grss.egg-info/dependency_links.txt +0 -0
- {grss-4.2.0 → grss-4.3.2}/grss.egg-info/requires.txt +0 -0
- {grss-4.2.0 → grss-4.3.2}/grss.egg-info/top_level.txt +0 -0
- {grss-4.2.0 → grss-4.3.2}/include/approach.h +0 -0
- {grss-4.2.0 → grss-4.3.2}/include/elements.h +0 -0
- {grss-4.2.0 → grss-4.3.2}/include/force.h +0 -0
- {grss-4.2.0 → grss-4.3.2}/include/interpolate.h +0 -0
- {grss-4.2.0 → grss-4.3.2}/include/observe.h +0 -0
- {grss-4.2.0 → grss-4.3.2}/include/parallel.h +0 -0
- {grss-4.2.0 → grss-4.3.2}/include/pck.h +0 -0
- {grss-4.2.0 → grss-4.3.2}/include/spk.h +0 -0
- {grss-4.2.0 → grss-4.3.2}/include/timeconvert.h +0 -0
- {grss-4.2.0 → grss-4.3.2}/include/utilities.h +0 -0
- {grss-4.2.0 → grss-4.3.2}/pyproject.toml +0 -0
- {grss-4.2.0 → grss-4.3.2}/requirements.txt +0 -0
- {grss-4.2.0 → grss-4.3.2}/setup.cfg +0 -0
- {grss-4.2.0 → grss-4.3.2}/setup.py +0 -0
- {grss-4.2.0 → grss-4.3.2}/src/CMakeLists.txt +0 -0
- {grss-4.2.0 → grss-4.3.2}/src/approach.cpp +0 -0
- {grss-4.2.0 → grss-4.3.2}/src/elements.cpp +0 -0
- {grss-4.2.0 → grss-4.3.2}/src/parallel.cpp +0 -0
- {grss-4.2.0 → grss-4.3.2}/src/spk.cpp +0 -0
- {grss-4.2.0 → grss-4.3.2}/src/timeconvert.cpp +0 -0
- {grss-4.2.0 → grss-4.3.2}/src/utilities.cpp +0 -0
- {grss-4.2.0 → grss-4.3.2}/tests/cpp/prop/CMakeLists.txt +0 -0
- {grss-4.2.0 → grss-4.3.2}/tests/cpp/prop/apophis.cpp +0 -0
- {grss-4.2.0 → grss-4.3.2}/tests/cpp/prop/didymos.cpp +0 -0
- {grss-4.2.0 → grss-4.3.2}/tests/cpp/prop/spk_map.cpp +0 -0
- {grss-4.2.0 → grss-4.3.2}/tests/python/fit/chesley.ipynb +0 -0
- {grss-4.2.0 → grss-4.3.2}/tests/python/fit/eggl.ipynb +0 -0
- {grss-4.2.0 → grss-4.3.2}/tests/python/fit/farnocchia.ipynb +0 -0
- {grss-4.2.0 → grss-4.3.2}/tests/python/fit/shantanunaidu.ipynb +0 -0
- {grss-4.2.0 → grss-4.3.2}/tests/python/prop/apophis.ipynb +0 -0
- {grss-4.2.0 → grss-4.3.2}/tests/python/prop/didymos.ipynb +0 -0
- {grss-4.2.0 → grss-4.3.2}/tests/python/prop/icarus.ipynb +0 -0
- {grss-4.2.0 → grss-4.3.2}/tests/python/prop/moshup.ipynb +0 -0
- {grss-4.2.0 → grss-4.3.2}/tests/python/run_tests.sh +0 -0
|
@@ -479,7 +479,8 @@ def apply_debiasing_scheme(obs_df, lowres, verbose):
|
|
|
479
479
|
unbias_counter += len(group)
|
|
480
480
|
continue
|
|
481
481
|
if cat_code not in biased_catalogs:
|
|
482
|
-
|
|
482
|
+
if verbose:
|
|
483
|
+
print(f"\tUnknown star catalog: {cat}")
|
|
483
484
|
obs_df.loc[group.index, 'biasRA'] = 0.0
|
|
484
485
|
obs_df.loc[group.index, 'biasDec'] = 0.0
|
|
485
486
|
continue
|
|
@@ -71,7 +71,6 @@ def add_radar_obs(obs_df, t_min_tdb=None, t_max_tdb=None, verbose=False):
|
|
|
71
71
|
print(f"Read in {num_obs} radar observations from JPL radar API.")
|
|
72
72
|
data = raw_data['data']
|
|
73
73
|
time_range_count = 0
|
|
74
|
-
pre1972_count = 0
|
|
75
74
|
for i in range(num_obs):
|
|
76
75
|
obs = data[num_obs-i-1]
|
|
77
76
|
date = Time(obs[1], format='iso', scale='utc')
|
|
@@ -105,14 +104,6 @@ def add_radar_obs(obs_df, t_min_tdb=None, t_max_tdb=None, verbose=False):
|
|
|
105
104
|
obs_df.loc[idx,'frq'] = freq
|
|
106
105
|
obs_df.loc[idx,'sigDelay'] = obs_sigma if delay else np.nan
|
|
107
106
|
obs_df.loc[idx,'sigDoppler'] = obs_sigma if doppler else np.nan
|
|
108
|
-
if date.utc.mjd < 41317:
|
|
109
|
-
pre1972_count += 1
|
|
110
|
-
obs_df.loc[idx,'selAst'] = 'd'
|
|
111
|
-
if pre1972_count > 0:
|
|
112
|
-
print(f"\tWARNING: {pre1972_count} radar observations were taken before 1972.\n"
|
|
113
|
-
"\t\tThese residuals cannot be reliably computed because high-accuracy Earth "
|
|
114
|
-
"orientation kernels are not available for this time period.\n"
|
|
115
|
-
"\t\tForce deleting them...")
|
|
116
107
|
if verbose:
|
|
117
108
|
print(f"\tFiltered to {num_obs-time_range_count} observations that satisfy the "
|
|
118
109
|
"time range constraints.")
|
|
@@ -46,7 +46,7 @@ class IterationParams:
|
|
|
46
46
|
self.covariance = covariance
|
|
47
47
|
variance = np.sqrt(np.diag(self.covariance))
|
|
48
48
|
self.variance = dict(zip([f'var_{k}' for k in self.x_nom.keys()], variance))
|
|
49
|
-
self.obs = obs
|
|
49
|
+
self.obs = obs.copy()
|
|
50
50
|
force_del_idx = self.obs[self.obs['selAst'] == 'd'].index
|
|
51
51
|
auto_del_idx = self.obs[self.obs['selAst'] == 'D'].index
|
|
52
52
|
self.rejected_idx = np.concatenate((force_del_idx, auto_del_idx))
|
|
@@ -549,7 +549,7 @@ class FitSimulation:
|
|
|
549
549
|
self._compute_obs_weights()
|
|
550
550
|
self.n_iter = 0
|
|
551
551
|
self.n_iter_max = n_iter_max
|
|
552
|
-
self.iters = []
|
|
552
|
+
self.iters = [[]]
|
|
553
553
|
self.de_kernel = de_kernel
|
|
554
554
|
self.de_kernel_path = default_kernel_path
|
|
555
555
|
self.analytic_partials = False
|
|
@@ -561,6 +561,12 @@ class FitSimulation:
|
|
|
561
561
|
for key in nongrav_info:
|
|
562
562
|
self.fixed_propsim_params[key] = nongrav_info[key]
|
|
563
563
|
self.fixed_propsim_params['events'] = events if events is not None else []
|
|
564
|
+
self.prior_est = None
|
|
565
|
+
self.prior_sigs = None
|
|
566
|
+
self._priors_given = False
|
|
567
|
+
self._xbar0 = None
|
|
568
|
+
self._info0 = None
|
|
569
|
+
self._prior_constant = None
|
|
564
570
|
self.reject_outliers = True
|
|
565
571
|
self.reject_criteria = [3.0, 2.8]
|
|
566
572
|
# number of rejected obs is the count of ['D', 'd'] in selAst
|
|
@@ -621,6 +627,38 @@ class FitSimulation:
|
|
|
621
627
|
raise ValueError(msg)
|
|
622
628
|
return None
|
|
623
629
|
|
|
630
|
+
def _check_priors(self):
|
|
631
|
+
"""
|
|
632
|
+
Check the prior estimates and sigmas provided by the user
|
|
633
|
+
and make sure they are valid.
|
|
634
|
+
|
|
635
|
+
Returns
|
|
636
|
+
-------
|
|
637
|
+
None : NoneType
|
|
638
|
+
None
|
|
639
|
+
|
|
640
|
+
Raises
|
|
641
|
+
------
|
|
642
|
+
ValueError
|
|
643
|
+
If the prior estimates and sigmas do not have the same length.
|
|
644
|
+
"""
|
|
645
|
+
# check that the keys in self.x_init are the same as in self.prior_est and self.prior_sigs
|
|
646
|
+
if self.prior_est is not None and self.prior_sigs is not None:
|
|
647
|
+
self._priors_given = True
|
|
648
|
+
if len(self.prior_est) != len(self.prior_sigs):
|
|
649
|
+
raise ValueError("Prior estimates and sigmas must have the same length.")
|
|
650
|
+
for key in self.prior_est.keys():
|
|
651
|
+
if key not in self.x_init:
|
|
652
|
+
raise ValueError(f"Key {key} not found in initial state vector.")
|
|
653
|
+
self._info0 = np.zeros_like(self.covariance_init)
|
|
654
|
+
self._xbar0 = np.zeros(self.n_fit)
|
|
655
|
+
for key in self.prior_est.keys():
|
|
656
|
+
idx = list(self.x_init.keys()).index(key)
|
|
657
|
+
self._info0[idx, idx] = 1/self.prior_sigs[key]**2
|
|
658
|
+
self._xbar0[idx] = self.prior_est[key] - self.x_init[key]
|
|
659
|
+
self._prior_constant = list(self.x_nom.values())+self._xbar0
|
|
660
|
+
return None
|
|
661
|
+
|
|
624
662
|
def _flatten_and_clean(self, arr):
|
|
625
663
|
"""
|
|
626
664
|
Flatten an array and remove any NaN values.
|
|
@@ -653,14 +691,18 @@ class FitSimulation:
|
|
|
653
691
|
weights = self.simulated_obs['weights']
|
|
654
692
|
if not len(times) == len(modes) == len(weights):
|
|
655
693
|
raise ValueError("Simulated observation data must have the same length.")
|
|
694
|
+
perm_id = self.obs['permID'][0]
|
|
695
|
+
prov_id = self.obs['provID'][0]
|
|
656
696
|
for i, time in enumerate(times):
|
|
657
697
|
idx = len(self.obs)
|
|
658
698
|
mode = modes[i]
|
|
659
699
|
if mode not in {'SIM_CCD', 'SIM_OCC', 'SIM_RAD_DEL', 'SIM_RAD_DOP'}:
|
|
660
700
|
raise ValueError(f"Unknown simulated observation mode {mode}.")
|
|
661
701
|
weight = weights[i]
|
|
662
|
-
|
|
663
|
-
|
|
702
|
+
if isinstance(perm_id, str):
|
|
703
|
+
self.obs.loc[idx, 'permID'] = f'SIM_{perm_id}'
|
|
704
|
+
if isinstance(prov_id, str):
|
|
705
|
+
self.obs.loc[idx, 'provID'] = f'SIM_{prov_id}'
|
|
664
706
|
self.obs.loc[idx, 'obsTime'] = f'{time.utc.isot}Z'
|
|
665
707
|
self.obs.loc[idx, 'obsTimeMJD'] = time.utc.mjd
|
|
666
708
|
self.obs.loc[idx, 'obsTimeMJDTDB'] = time.tdb.mjd
|
|
@@ -801,7 +843,7 @@ class FitSimulation:
|
|
|
801
843
|
"""
|
|
802
844
|
# pylint: disable=no-member
|
|
803
845
|
t_eval_past = self.obs.obsTimeMJD.values[self.past_obs_idx]
|
|
804
|
-
tf_past = np.min(
|
|
846
|
+
tf_past = np.min(self.obs.obsTimeMJDTDB.values[self.past_obs_idx])
|
|
805
847
|
prop_sim_past = libgrss.PropSimulation(name, self.t_sol,
|
|
806
848
|
self.de_kernel, self.de_kernel_path)
|
|
807
849
|
prop_sim_past.tEvalMargin = 1.0
|
|
@@ -845,7 +887,7 @@ class FitSimulation:
|
|
|
845
887
|
"""
|
|
846
888
|
# pylint: disable=no-member
|
|
847
889
|
t_eval_future = self.obs.obsTimeMJD.values[self.future_obs_idx]
|
|
848
|
-
tf_future = np.max(
|
|
890
|
+
tf_future = np.max(self.obs.obsTimeMJDTDB.values[self.future_obs_idx])
|
|
849
891
|
prop_sim_future = libgrss.PropSimulation(name, self.t_sol,
|
|
850
892
|
self.de_kernel, self.de_kernel_path)
|
|
851
893
|
prop_sim_future.tEvalMargin = 1.0
|
|
@@ -986,12 +1028,24 @@ class FitSimulation:
|
|
|
986
1028
|
events = []
|
|
987
1029
|
for i in range(len(self.fixed_propsim_params['events'])):
|
|
988
1030
|
fixed_event = tuple(self.fixed_propsim_params['events'][i])
|
|
1031
|
+
fixed_event_impulsive = len(fixed_event) == 5
|
|
1032
|
+
fixed_event_continuous = len(fixed_event) == 6
|
|
1033
|
+
if fixed_event_continuous and fixed_event[4] == 0.0:
|
|
1034
|
+
raise ValueError("Continuous event time constant cannot be 0.")
|
|
1035
|
+
assert fixed_event_impulsive or fixed_event_continuous
|
|
989
1036
|
event = [None]*5
|
|
990
|
-
event[0] = fixed_event[0]
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
1037
|
+
event[0] = fixed_event[0] # time of delta-v
|
|
1038
|
+
if fixed_event_impulsive:
|
|
1039
|
+
event[1] = x_dict[f"dvx{i}"] if f"dvx{i}" in x_dict.keys() else fixed_event[1]
|
|
1040
|
+
event[2] = x_dict[f"dvy{i}"] if f"dvy{i}" in x_dict.keys() else fixed_event[2]
|
|
1041
|
+
event[3] = x_dict[f"dvz{i}"] if f"dvz{i}" in x_dict.keys() else fixed_event[3]
|
|
1042
|
+
event[4] = x_dict[f"mult{i}"] if f"mult{i}" in x_dict.keys() else fixed_event[4]
|
|
1043
|
+
elif fixed_event_continuous:
|
|
1044
|
+
event[1] = x_dict[f"ax{i}"] if f"ax{i}" in x_dict.keys() else fixed_event[1]
|
|
1045
|
+
event[2] = x_dict[f"ay{i}"] if f"ay{i}" in x_dict.keys() else fixed_event[2]
|
|
1046
|
+
event[3] = x_dict[f"az{i}"] if f"az{i}" in x_dict.keys() else fixed_event[3]
|
|
1047
|
+
event[4] = x_dict[f"dt{i}"] if f"dt{i}" in x_dict.keys() else fixed_event[4]
|
|
1048
|
+
event.append(fixed_event[5])
|
|
995
1049
|
events.append(tuple(event))
|
|
996
1050
|
return events
|
|
997
1051
|
|
|
@@ -1017,16 +1071,42 @@ class FitSimulation:
|
|
|
1017
1071
|
prop_sim_future : libgrss.PropSimulation object
|
|
1018
1072
|
PropSimulation object for the future.
|
|
1019
1073
|
"""
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1074
|
+
est_keys = self.x_nom.keys()
|
|
1075
|
+
for i, event_list in enumerate(events):
|
|
1076
|
+
t_event = event_list[0]
|
|
1077
|
+
dvx_or_ax = event_list[1]
|
|
1078
|
+
dvy_or_ay = event_list[2]
|
|
1079
|
+
dvz_or_az = event_list[3]
|
|
1080
|
+
multiplier_or_dt = event_list[4]
|
|
1081
|
+
continuous_flag = event_list[5] if len(event_list) == 6 else False
|
|
1082
|
+
event = libgrss.Event()
|
|
1083
|
+
event.t = t_event
|
|
1084
|
+
event.bodyName = integ_body.name
|
|
1085
|
+
event.isContinuous = continuous_flag
|
|
1086
|
+
if continuous_flag:
|
|
1087
|
+
event.expAccel0 = [dvx_or_ax, dvy_or_ay, dvz_or_az]
|
|
1088
|
+
event.tau = multiplier_or_dt
|
|
1089
|
+
event.expAccel0Est = (
|
|
1090
|
+
f"ax{i}" in est_keys and
|
|
1091
|
+
f"ay{i}" in est_keys and
|
|
1092
|
+
f"az{i}" in est_keys
|
|
1093
|
+
)
|
|
1094
|
+
event.tauEst = f"dt{i}" in est_keys
|
|
1095
|
+
event.eventEst = event.expAccel0Est or event.tauEst
|
|
1096
|
+
else:
|
|
1097
|
+
event.deltaV = [dvx_or_ax, dvy_or_ay, dvz_or_az]
|
|
1098
|
+
event.multiplier = multiplier_or_dt
|
|
1099
|
+
event.deltaVEst = (
|
|
1100
|
+
f"dvx{i}" in est_keys and
|
|
1101
|
+
f"dvy{i}" in est_keys and
|
|
1102
|
+
f"dvz{i}" in est_keys
|
|
1103
|
+
)
|
|
1104
|
+
event.multiplierEst = f"mult{i}" in est_keys
|
|
1105
|
+
event.eventEst = event.deltaVEst or event.multiplierEst
|
|
1026
1106
|
if t_event < self.t_sol:
|
|
1027
|
-
prop_sim_past.add_event(
|
|
1107
|
+
prop_sim_past.add_event(event)
|
|
1028
1108
|
else:
|
|
1029
|
-
prop_sim_future.add_event(
|
|
1109
|
+
prop_sim_future.add_event(event)
|
|
1030
1110
|
return prop_sim_past, prop_sim_future
|
|
1031
1111
|
|
|
1032
1112
|
def _get_perturbed_state(self, key):
|
|
@@ -1055,6 +1135,7 @@ class FitSimulation:
|
|
|
1055
1135
|
fd_delta : float
|
|
1056
1136
|
Finite difference perturbation.
|
|
1057
1137
|
"""
|
|
1138
|
+
fd_pert = np.inf
|
|
1058
1139
|
if self.fit_cartesian:
|
|
1059
1140
|
if key in {'x', 'y', 'z'}:
|
|
1060
1141
|
fd_pert = 1e-8
|
|
@@ -1074,6 +1155,14 @@ class FitSimulation:
|
|
|
1074
1155
|
fd_pert = 1e0
|
|
1075
1156
|
if key[:3] in {'dvx', 'dvy', 'dvz'}:
|
|
1076
1157
|
fd_pert = 1e-11
|
|
1158
|
+
if key[:2] in {'ax', 'ay', 'az'}:
|
|
1159
|
+
fd_pert = 1e-11
|
|
1160
|
+
if key[:2] == 'dt':
|
|
1161
|
+
# 10 percent of the time constant
|
|
1162
|
+
fd_pert = 0.1*self.x_nom[key]
|
|
1163
|
+
if fd_pert == np.inf:
|
|
1164
|
+
raise ValueError("Finite difference perturbation not defined"
|
|
1165
|
+
" for the given state parameter.")
|
|
1077
1166
|
x_plus = self.x_nom.copy()
|
|
1078
1167
|
x_minus = self.x_nom.copy()
|
|
1079
1168
|
# fd_pert = finite difference perturbation to nominal state for calculating derivatives
|
|
@@ -1285,7 +1374,7 @@ class FitSimulation:
|
|
|
1285
1374
|
radius_nonzero = self.fixed_propsim_params['radius'] != 0.0
|
|
1286
1375
|
fac = 0.0
|
|
1287
1376
|
if radius_nonzero:
|
|
1288
|
-
rel_dists = np.linalg.norm(
|
|
1377
|
+
rel_dists = np.linalg.norm([state[:3] for state in state_eval], axis=1)
|
|
1289
1378
|
lmbda = 0.3 # from fuentes-munoz et al. 2024
|
|
1290
1379
|
au2m = 1.495978707e11
|
|
1291
1380
|
fac = (lmbda*self.fixed_propsim_params['radius']/au2m/rel_dists*180/np.pi*3600)**2
|
|
@@ -1296,7 +1385,7 @@ class FitSimulation:
|
|
|
1296
1385
|
| special_codes["spacecraft"]
|
|
1297
1386
|
)
|
|
1298
1387
|
for i in range(len(self.observer_info_lengths)):
|
|
1299
|
-
if modes[i] not in {'RAD', 'SIM_RAD_DEL', 'SIM_RAD_DOP', 'SIM_OCC'}:
|
|
1388
|
+
if modes[i] not in {'RAD', 'SIM_RAD_DEL', 'SIM_RAD_DOP', 'SIM_CCD', 'SIM_OCC'}:
|
|
1300
1389
|
sig_ra = sig_ra_vals[i]
|
|
1301
1390
|
sig_dec = sig_dec_vals[i]
|
|
1302
1391
|
sig_corr = sig_corr_vals[i]
|
|
@@ -1357,7 +1446,8 @@ class FitSimulation:
|
|
|
1357
1446
|
size = 1
|
|
1358
1447
|
else:
|
|
1359
1448
|
raise ValueError("Observer info length not recognized.")
|
|
1360
|
-
part
|
|
1449
|
+
# part is partial of observable with respect to state at observation time
|
|
1450
|
+
part = np.zeros((size, 6))
|
|
1361
1451
|
if self.past_obs_exist and i < len_past_idx:
|
|
1362
1452
|
sim_idx = i
|
|
1363
1453
|
optical_partials = past_optical_partials
|
|
@@ -1368,14 +1458,16 @@ class FitSimulation:
|
|
|
1368
1458
|
optical_partials = future_optical_partials
|
|
1369
1459
|
radar_partials = future_radar_partials
|
|
1370
1460
|
state = future_states
|
|
1371
|
-
stm
|
|
1461
|
+
# stm is partial of state at observation time with respect to nominal state
|
|
1462
|
+
stm = libgrss.reconstruct_stm(state[sim_idx][6:])[:6]
|
|
1372
1463
|
if is_optical:
|
|
1373
1464
|
part[0, :6] = optical_partials[sim_idx, :6]*cos_dec[i]
|
|
1374
1465
|
part[1, :6] = optical_partials[sim_idx, 6:12]
|
|
1375
1466
|
else:
|
|
1376
1467
|
part[0, :6] = radar_partials[sim_idx, :6]
|
|
1468
|
+
# partial is partial of observable with respect to nominal state
|
|
1377
1469
|
partial = part @ stm
|
|
1378
|
-
partials[partials_idx:partials_idx+size, :] = partial
|
|
1470
|
+
partials[partials_idx:partials_idx+size, :partial.shape[1]] = partial
|
|
1379
1471
|
partials_idx += size
|
|
1380
1472
|
return partials
|
|
1381
1473
|
|
|
@@ -1533,6 +1625,7 @@ class FitSimulation:
|
|
|
1533
1625
|
chi_sq = 0
|
|
1534
1626
|
j = 0
|
|
1535
1627
|
sel_ast = self.obs['selAst'].values
|
|
1628
|
+
res_chisq_vals = np.nan*np.ones(len(self.observer_info_lengths))
|
|
1536
1629
|
for i, obs_info_len in enumerate(self.observer_info_lengths):
|
|
1537
1630
|
if obs_info_len in {4, 7}:
|
|
1538
1631
|
size = 2
|
|
@@ -1541,8 +1634,8 @@ class FitSimulation:
|
|
|
1541
1634
|
else:
|
|
1542
1635
|
raise ValueError("Observer info length not recognized.")
|
|
1543
1636
|
resid = residuals[i]
|
|
1544
|
-
# calculate chi-squared for each residual
|
|
1545
|
-
if
|
|
1637
|
+
# calculate chi-squared for each residual if after the first iteration
|
|
1638
|
+
if self.n_iter > 1:
|
|
1546
1639
|
obs_cov = self.obs_cov[i]
|
|
1547
1640
|
obs_partials = partials[j:j+size, :]
|
|
1548
1641
|
if sel_ast[i] in {'D', 'd'}:
|
|
@@ -1554,22 +1647,29 @@ class FitSimulation:
|
|
|
1554
1647
|
else:
|
|
1555
1648
|
resid_cov_det = resid_cov[0,0]*resid_cov[1,1] - resid_cov[0,1]*resid_cov[1,0]
|
|
1556
1649
|
resid_cov_inv = np.array([[resid_cov[1,1], -resid_cov[0,1]],
|
|
1557
|
-
|
|
1650
|
+
[-resid_cov[1,0], resid_cov[0,0]]])/resid_cov_det
|
|
1558
1651
|
residual_chi_squared = resid @ resid_cov_inv @ resid.T
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1652
|
+
res_chisq_vals[i] = residual_chi_squared
|
|
1653
|
+
# outlier rejection, only reject RA/Dec measurements
|
|
1654
|
+
if start_rejecting and size == 2:
|
|
1655
|
+
if abs(residual_chi_squared) > chi_reject**2 and sel_ast[i] not in {'a', 'd'}:
|
|
1656
|
+
if sel_ast[i] == 'A':
|
|
1657
|
+
self.num_rejected += 1
|
|
1658
|
+
sel_ast[i] = 'D'
|
|
1659
|
+
elif abs(residual_chi_squared) < chi_recover**2 and sel_ast[i] == 'D':
|
|
1660
|
+
sel_ast[i] = 'A'
|
|
1661
|
+
self.num_rejected -= 1
|
|
1568
1662
|
if sel_ast[i] not in {'D', 'd'}:
|
|
1569
1663
|
rms_u += resid @ resid.T
|
|
1570
1664
|
chi_sq += resid @ self.obs_weight[i] @ resid.T
|
|
1571
1665
|
j += size
|
|
1666
|
+
# # write res_chisq_vals to file if any values are negative
|
|
1667
|
+
# if np.any(res_chisq_vals < 0):
|
|
1668
|
+
# print("WARNING: Negative outlier rejection chi-squared values detected. "
|
|
1669
|
+
# "Writing to file 'warn_neg_chisq_vals.txt'.")
|
|
1670
|
+
# np.savetxt("warn_neg_chisq_vals.txt", res_chisq_vals, fmt="%.16f")
|
|
1572
1671
|
self.obs['selAst'] = sel_ast
|
|
1672
|
+
self.obs['resChi'] = res_chisq_vals**0.5
|
|
1573
1673
|
rejected_fraction = self.num_rejected/len(self.obs)
|
|
1574
1674
|
if rejected_fraction > 0.25:
|
|
1575
1675
|
print("WARNING: More than 25% of observations rejected. Consider changing the",
|
|
@@ -1651,8 +1751,12 @@ class FitSimulation:
|
|
|
1651
1751
|
delta_x : array
|
|
1652
1752
|
The state correction.
|
|
1653
1753
|
"""
|
|
1654
|
-
|
|
1655
|
-
|
|
1754
|
+
if self._priors_given:
|
|
1755
|
+
atwa = self._info0.copy()
|
|
1756
|
+
atwb = (self._info0 @ self._xbar0).copy()
|
|
1757
|
+
else:
|
|
1758
|
+
atwa = np.zeros((self.n_fit, self.n_fit))
|
|
1759
|
+
atwb = np.zeros(self.n_fit)
|
|
1656
1760
|
j = 0
|
|
1657
1761
|
sel_ast = self.obs['selAst'].values
|
|
1658
1762
|
for i, obs_info_len in enumerate(self.observer_info_lengths):
|
|
@@ -1670,11 +1774,11 @@ class FitSimulation:
|
|
|
1670
1774
|
j += size
|
|
1671
1775
|
# use pseudo-inverse if the data arc is less than 7 days
|
|
1672
1776
|
if self.obs.obsTimeMJD.max() - self.obs.obsTimeMJD.min() < 7.0:
|
|
1673
|
-
|
|
1777
|
+
self.covariance = np.linalg.pinv(atwa, rcond=1e-20, hermitian=True)
|
|
1674
1778
|
else:
|
|
1675
|
-
|
|
1676
|
-
delta_x =
|
|
1677
|
-
return delta_x.ravel()
|
|
1779
|
+
self.covariance = np.array(libgrss.matrix_inverse(atwa))
|
|
1780
|
+
delta_x = self.covariance @ atwb
|
|
1781
|
+
return delta_x.ravel()
|
|
1678
1782
|
|
|
1679
1783
|
def filter_lsq(self, verbose=True):
|
|
1680
1784
|
"""
|
|
@@ -1690,35 +1794,51 @@ class FitSimulation:
|
|
|
1690
1794
|
None : NoneType
|
|
1691
1795
|
None
|
|
1692
1796
|
"""
|
|
1797
|
+
self._check_priors()
|
|
1693
1798
|
start_rejecting = False
|
|
1694
1799
|
if verbose:
|
|
1695
1800
|
print("Iteration\t\tUnweighted RMS\t\tWeighted RMS",
|
|
1696
1801
|
"\t\tChi-squared\t\tReduced Chi-squared")
|
|
1802
|
+
delta_x = np.zeros(self.n_fit)
|
|
1697
1803
|
for i in range(self.n_iter_max):
|
|
1804
|
+
if self._priors_given:
|
|
1805
|
+
self._xbar0 -= delta_x
|
|
1806
|
+
a_priori_constant = list(self.x_nom.values())+self._xbar0
|
|
1807
|
+
a_priori_constant_violation = np.linalg.norm(a_priori_constant-self._prior_constant)
|
|
1808
|
+
if a_priori_constant_violation >= 1e-11:
|
|
1809
|
+
print(f"a priori constraint constant violation before iteration {i+1}: "
|
|
1810
|
+
f"{a_priori_constant_violation}. Solution may not be trustworthy. "
|
|
1811
|
+
"Violation for each estimated parameter:")
|
|
1812
|
+
print(dict(zip(self.x_nom.keys(), a_priori_constant-self._prior_constant)))
|
|
1698
1813
|
self.n_iter = i+1
|
|
1699
1814
|
# get residuals and partials
|
|
1700
1815
|
residuals, partials = self._get_residuals_and_partials()
|
|
1701
1816
|
# calculate rms and reject outliers here if desired
|
|
1702
1817
|
rms_u, rms_w, chi_sq = self._get_rms_and_reject_outliers(partials, residuals,
|
|
1703
1818
|
start_rejecting)
|
|
1704
|
-
# get
|
|
1705
|
-
curr_state =
|
|
1819
|
+
# get current state
|
|
1820
|
+
curr_state = list(self.x_nom.values())
|
|
1706
1821
|
# get state correction
|
|
1707
|
-
delta_x
|
|
1708
|
-
# get new covariance
|
|
1709
|
-
self.covariance = cov
|
|
1710
|
-
# get new state
|
|
1711
|
-
if i == 0:
|
|
1712
|
-
# add prefit iteration
|
|
1713
|
-
self._add_iteration(0, rms_u, rms_w, chi_sq)
|
|
1822
|
+
delta_x = self._get_lsq_state_correction(partials, residuals)
|
|
1714
1823
|
if self.constraint_dir is not None:
|
|
1715
1824
|
constr_hat = self.constraint_dir/np.linalg.norm(self.constraint_dir)
|
|
1716
1825
|
delta_x -= np.dot(delta_x, constr_hat)*constr_hat
|
|
1826
|
+
# make sure eccentricity is non-negative and
|
|
1827
|
+
# any keys starting with dt are 0.1 at minimum
|
|
1828
|
+
for key in self.x_nom:
|
|
1829
|
+
if self.fit_cometary and key == 'e':
|
|
1830
|
+
idx = list(self.x_nom.keys()).index(key)
|
|
1831
|
+
if curr_state[idx] + delta_x[idx] < 0.0:
|
|
1832
|
+
delta_x[idx] = -curr_state[idx]
|
|
1833
|
+
print("WARNING: Eccentricity is negative per least squares "
|
|
1834
|
+
"state correction. Setting to 0.0. This solution "
|
|
1835
|
+
"may not be trustworthy.")
|
|
1836
|
+
if key[:2] == 'dt':
|
|
1837
|
+
idx = list(self.x_nom.keys()).index(key)
|
|
1838
|
+
if curr_state[idx] + delta_x[idx] < 0.1:
|
|
1839
|
+
delta_x[idx] = 0.1 - curr_state[idx]
|
|
1840
|
+
# get new nominal state
|
|
1717
1841
|
next_state = curr_state + delta_x
|
|
1718
|
-
if self.fit_cometary and next_state[0] < 0.0:
|
|
1719
|
-
next_state[0] = 0.0
|
|
1720
|
-
print("WARNING: Eccentricity is negative per least squares state correction. "
|
|
1721
|
-
"Setting to 0.0. This solution may not be trustworthy.")
|
|
1722
1842
|
self.x_nom = dict(zip(self.x_nom.keys(), next_state))
|
|
1723
1843
|
# add iteration
|
|
1724
1844
|
self._add_iteration(i+1, rms_u, rms_w, chi_sq)
|
|
@@ -1744,11 +1864,17 @@ class FitSimulation:
|
|
|
1744
1864
|
print(msg)
|
|
1745
1865
|
if not self.reject_outliers:
|
|
1746
1866
|
break
|
|
1867
|
+
# add postfit iteration if converged
|
|
1868
|
+
if self.converged:
|
|
1869
|
+
residuals, partials = self._get_residuals_and_partials()
|
|
1870
|
+
rms_u, rms_w, chi_sq = self._get_rms_and_reject_outliers(partials, residuals,
|
|
1871
|
+
start_rejecting)
|
|
1872
|
+
self._add_iteration(self.n_iter+1, rms_u, rms_w, chi_sq)
|
|
1747
1873
|
if self.n_iter == self.n_iter_max and not self.converged:
|
|
1748
1874
|
print("WARNING: Maximum number of iterations reached without converging.")
|
|
1749
1875
|
return None
|
|
1750
1876
|
|
|
1751
|
-
def print_summary(self, iter_idx
|
|
1877
|
+
def print_summary(self, iter_idx=None, out_stream=sys.stdout):
|
|
1752
1878
|
"""
|
|
1753
1879
|
Prints a summary of the orbit fit calculations at a given iteration.
|
|
1754
1880
|
|
|
@@ -1762,10 +1888,14 @@ class FitSimulation:
|
|
|
1762
1888
|
None : NoneType
|
|
1763
1889
|
None
|
|
1764
1890
|
"""
|
|
1891
|
+
iter_idx = len(self.iters)-1 if iter_idx is None else iter_idx
|
|
1765
1892
|
data = self.iters[iter_idx]
|
|
1766
1893
|
arc = self.obs.obsTimeMJD.max() - self.obs.obsTimeMJD.min()
|
|
1767
|
-
str_to_print = "Summary of the orbit fit calculations
|
|
1768
|
-
|
|
1894
|
+
str_to_print = "Summary of the orbit fit calculations"
|
|
1895
|
+
if data.iter_number <= self.n_iter:
|
|
1896
|
+
str_to_print += f" after iteration {data.iter_number} (of {self.n_iter}):\n"
|
|
1897
|
+
else:
|
|
1898
|
+
str_to_print += " after postfit pass:\n"
|
|
1769
1899
|
str_to_print += "==============================================================\n"
|
|
1770
1900
|
str_to_print += f"RMS unweighted: {data.unweighted_rms}\n"
|
|
1771
1901
|
str_to_print += f"RMS weighted: {data.weighted_rms}\n"
|
|
@@ -1781,7 +1911,7 @@ class FitSimulation:
|
|
|
1781
1911
|
str_to_print += "\t\t\tUncertainty\t\t\tChange\t\t\t\tChange (sigma)\n"
|
|
1782
1912
|
init_variance = np.sqrt(np.diag(self.covariance_init))
|
|
1783
1913
|
final_variance = np.sqrt(np.diag(self.covariance))
|
|
1784
|
-
init_sol = self.
|
|
1914
|
+
init_sol = self.x_init
|
|
1785
1915
|
final_sol = data.x_nom
|
|
1786
1916
|
with np.errstate(divide='ignore'):
|
|
1787
1917
|
for i, key in enumerate(init_sol.keys()):
|
|
@@ -1817,7 +1947,9 @@ class FitSimulation:
|
|
|
1817
1947
|
"""
|
|
1818
1948
|
plt.rcParams['font.size'] = 12
|
|
1819
1949
|
plt.rcParams['axes.labelsize'] = 12
|
|
1820
|
-
ticks = np.arange(1, self.n_iter+
|
|
1950
|
+
ticks = np.arange(1, self.n_iter+2, 1)
|
|
1951
|
+
labels = list(ticks)
|
|
1952
|
+
labels[-1] = "Postfit"
|
|
1821
1953
|
start_idx = 1
|
|
1822
1954
|
iters_for_plot = self.iters[start_idx:]
|
|
1823
1955
|
final_iter = iters_for_plot[-1]
|
|
@@ -1827,7 +1959,8 @@ class FitSimulation:
|
|
|
1827
1959
|
[iteration.unweighted_rms for iteration in iters_for_plot],
|
|
1828
1960
|
label=f"Final Unweighted RMS={final_iter.unweighted_rms:.3e}")
|
|
1829
1961
|
plt.xticks(ticks)
|
|
1830
|
-
plt.
|
|
1962
|
+
plt.gca().set_xticklabels(labels)
|
|
1963
|
+
plt.xlabel("Iteration")
|
|
1831
1964
|
plt.ylabel("Unweighted RMS")
|
|
1832
1965
|
plt.legend()
|
|
1833
1966
|
plt.subplot(2, 2, 2)
|
|
@@ -1835,7 +1968,8 @@ class FitSimulation:
|
|
|
1835
1968
|
[iteration.weighted_rms for iteration in iters_for_plot],
|
|
1836
1969
|
label=f"Final Weighted RMS={final_iter.weighted_rms:.3e}")
|
|
1837
1970
|
plt.xticks(ticks)
|
|
1838
|
-
plt.
|
|
1971
|
+
plt.gca().set_xticklabels(labels)
|
|
1972
|
+
plt.xlabel("Iteration")
|
|
1839
1973
|
plt.ylabel("Weighted RMS")
|
|
1840
1974
|
plt.legend()
|
|
1841
1975
|
plt.subplot(2, 2, 3)
|
|
@@ -1843,7 +1977,8 @@ class FitSimulation:
|
|
|
1843
1977
|
[iteration.chi_squared for iteration in iters_for_plot],
|
|
1844
1978
|
label=fr"Final $\chi^2$={final_iter.chi_squared:.3e}")
|
|
1845
1979
|
plt.xticks(ticks)
|
|
1846
|
-
plt.
|
|
1980
|
+
plt.gca().set_xticklabels(labels)
|
|
1981
|
+
plt.xlabel("Iteration")
|
|
1847
1982
|
plt.ylabel(r"$\chi^2$")
|
|
1848
1983
|
plt.legend()
|
|
1849
1984
|
plt.subplot(2, 2, 4)
|
|
@@ -1851,7 +1986,8 @@ class FitSimulation:
|
|
|
1851
1986
|
[iteration.reduced_chi_squared for iteration in iters_for_plot],
|
|
1852
1987
|
label=fr"Final Reduced $\chi^2$={final_iter.reduced_chi_squared:.3e}")
|
|
1853
1988
|
plt.xticks(ticks)
|
|
1854
|
-
plt.
|
|
1989
|
+
plt.gca().set_xticklabels(labels)
|
|
1990
|
+
plt.xlabel("Iteration")
|
|
1855
1991
|
plt.ylabel(r"Reduced $\chi^2$")
|
|
1856
1992
|
plt.legend()
|
|
1857
1993
|
plt.tight_layout()
|
|
@@ -1890,6 +2026,8 @@ class FitSimulation:
|
|
|
1890
2026
|
'w': 'deg', 'i': 'deg', 'x': 'AU', 'y': 'AU', 'z': 'AU',
|
|
1891
2027
|
'vx': 'AU/day', 'vy': 'AU/day', 'vz': 'AU/day',
|
|
1892
2028
|
'a1': 'AU/day^2', 'a2': 'AU/day^2', 'a3': 'AU/day^2',
|
|
2029
|
+
'dv': 'AU/day', 'ax': 'AU/day^2', 'ay': 'AU/day^2', 'az': 'AU/day^2',
|
|
2030
|
+
'dt': 'day'
|
|
1893
2031
|
}
|
|
1894
2032
|
with open(filename, 'x', encoding='utf-8') as f:
|
|
1895
2033
|
f.write(section_full + '\n')
|
|
@@ -1928,12 +2066,25 @@ class FitSimulation:
|
|
|
1928
2066
|
if key in ['om', 'w', 'i']:
|
|
1929
2067
|
val *= 180/np.pi
|
|
1930
2068
|
sig *= 180/np.pi
|
|
1931
|
-
if key in
|
|
2069
|
+
if key[:2] in {'a1', 'a2', 'a3', 'dv', 'ax', 'ay', 'az'}:
|
|
1932
2070
|
f.write(f"{key.upper()}: {val:.11e} \u00B1 {sig:.11e}")
|
|
1933
2071
|
else:
|
|
1934
2072
|
f.write(f"{key.upper()}: {val:.11f} \u00B1 {sig:.11f}")
|
|
1935
|
-
if key in units_dict:
|
|
1936
|
-
f.write(f" {units_dict[key]}")
|
|
2073
|
+
if key[:2] in units_dict:
|
|
2074
|
+
f.write(f" {units_dict[key[:2]]}")
|
|
2075
|
+
if self._priors_given and key in self.prior_est:
|
|
2076
|
+
p_val = self.prior_est[key]
|
|
2077
|
+
p_sig = self.prior_sigs[key]
|
|
2078
|
+
if key in ['om', 'w', 'i']:
|
|
2079
|
+
p_val *= 180/np.pi
|
|
2080
|
+
p_sig *= 180/np.pi
|
|
2081
|
+
if key[:2] in {'a1', 'a2', 'a3', 'dv', 'ax', 'ay', 'az'}:
|
|
2082
|
+
f.write(f" (a priori: {p_val:.11e} \u00B1 {p_sig:.11e}")
|
|
2083
|
+
else:
|
|
2084
|
+
f.write(f" (a priori: {p_val:.11f} \u00B1 {p_sig:.11f}")
|
|
2085
|
+
if key[:2] in units_dict:
|
|
2086
|
+
f.write(f" {units_dict[key[:2]]}")
|
|
2087
|
+
f.write(")")
|
|
1937
2088
|
f.write("\n")
|
|
1938
2089
|
f.write("\n")
|
|
1939
2090
|
f.write("Initial Covariance Matrix:\n")
|
|
@@ -1955,12 +2106,25 @@ class FitSimulation:
|
|
|
1955
2106
|
if key in ['om', 'w', 'i']:
|
|
1956
2107
|
val *= 180/np.pi
|
|
1957
2108
|
sig *= 180/np.pi
|
|
1958
|
-
if key in
|
|
2109
|
+
if key[:2] in {'a1', 'a2', 'a3', 'dv', 'ax', 'ay', 'az'}:
|
|
1959
2110
|
f.write(f"{key.upper()}: {val:.11e} \u00B1 {sig:.11e}")
|
|
1960
2111
|
else:
|
|
1961
2112
|
f.write(f"{key.upper()}: {val:.11f} \u00B1 {sig:.11f}")
|
|
1962
|
-
if key in units_dict:
|
|
1963
|
-
f.write(f" {units_dict[key]}")
|
|
2113
|
+
if key[:2] in units_dict:
|
|
2114
|
+
f.write(f" {units_dict[key[:2]]}")
|
|
2115
|
+
if self._priors_given and key in self.prior_est:
|
|
2116
|
+
p_val = self.prior_est[key]
|
|
2117
|
+
p_sig = self.prior_sigs[key]
|
|
2118
|
+
if key in ['om', 'w', 'i']:
|
|
2119
|
+
p_val *= 180/np.pi
|
|
2120
|
+
p_sig *= 180/np.pi
|
|
2121
|
+
if key[:2] in {'a1', 'a2', 'a3', 'dv', 'ax', 'ay', 'az'}:
|
|
2122
|
+
f.write(f" (a priori: {p_val:.11e} \u00B1 {p_sig:.11e}")
|
|
2123
|
+
else:
|
|
2124
|
+
f.write(f" (a priori: {p_val:.11f} \u00B1 {p_sig:.11f}")
|
|
2125
|
+
if key[:2] in units_dict:
|
|
2126
|
+
f.write(f" {units_dict[key[:2]]}")
|
|
2127
|
+
f.write(")")
|
|
1964
2128
|
f.write("\n")
|
|
1965
2129
|
f.write("\n")
|
|
1966
2130
|
f.write("Final Covariance Matrix:\n")
|
|
@@ -2015,8 +2179,11 @@ class FitSimulation:
|
|
|
2015
2179
|
f.write(f'{iter_summary_buff}{iter_summary_str}{iter_summary_buff}\n')
|
|
2016
2180
|
f.write(subsection_full + '\n')
|
|
2017
2181
|
for itrn in self.iters[1:]:
|
|
2018
|
-
f.write("Summary of the orbit fit calculations
|
|
2019
|
-
|
|
2182
|
+
f.write("Summary of the orbit fit calculations")
|
|
2183
|
+
if itrn.iter_number <= self.n_iter:
|
|
2184
|
+
f.write(f" after iteration {itrn.iter_number} (of {self.n_iter}):\n")
|
|
2185
|
+
else:
|
|
2186
|
+
f.write(" after postfit pass:\n")
|
|
2020
2187
|
f.write(f"RMS unweighted: {itrn.unweighted_rms}\n")
|
|
2021
2188
|
f.write(f"RMS weighted: {itrn.weighted_rms}\n")
|
|
2022
2189
|
f.write(f"chi-squared: {itrn.chi_squared}\n")
|
|
@@ -2025,7 +2192,7 @@ class FitSimulation:
|
|
|
2025
2192
|
f.write(f'{"Variable":<8}{"Initial Value":>20}')
|
|
2026
2193
|
f.write(f'{"Uncertainty":>20}{"Fitted Value":>20}')
|
|
2027
2194
|
f.write(f'{"Uncertainty":>20}{"Change":>20}{"Change":>8}'+' (\u03C3)\n')
|
|
2028
|
-
init_sol = self.
|
|
2195
|
+
init_sol = self.x_init
|
|
2029
2196
|
final_sol = itrn.x_nom
|
|
2030
2197
|
with np.errstate(divide='ignore'):
|
|
2031
2198
|
for i, key in enumerate(init_sol.keys()):
|
|
@@ -2046,7 +2213,7 @@ class FitSimulation:
|
|
|
2046
2213
|
f.write(f'{final_val-init_val:+18.11e}{half_tab}')
|
|
2047
2214
|
f.write(f'{(final_val-init_val)/init_unc:+10.3f}\n')
|
|
2048
2215
|
f.write("\n")
|
|
2049
|
-
if itrn.iter_number
|
|
2216
|
+
if itrn.iter_number <= self.n_iter:
|
|
2050
2217
|
f.write(subsection_full + '\n')
|
|
2051
2218
|
|
|
2052
2219
|
mean_0 = np.array(list(self.x_init.values()))
|