00001
00016
00017
00018
00019
00020 #include "dyn.h"
00021
00022 #define ITERAC 1.0e-12 // Iteration accuracy for Kepler's equation
00023 #define MAXITER 100 // Maximum number of iterations
00024
00025 #define TRIG_TOL 1.e-12 // Tolerance in sine and cosine functions
00026
00027 #define SQRT_TRIG_TOL 1.e-6 // \/ Tolerance in sine and cosine functions
00028
00029 #define INFINITY VERY_LARGE_NUMBER
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064 #ifndef TOOLBOX
00065
00066 local real cos_to_sin(real c)
00067 {
00068 if (abs(c) >= 1) return 0;
00069
00070 real eps = 1 - abs(c);
00071 if (eps > TRIG_TOL) return sqrt(1 - c*c);
00072
00073 return sqrt(2*eps);
00074 }
00075
00076
00077
00078
00079
00080 static int kepler_tolerance_level = 0;
00081 static bool print_trig_warning = false;
00082
00083
00084
00085
00086
00087
00088
00089 void set_kepler_tolerance(int i)
00090 {
00091 if (i < 0) i = 0;
00092 if (i > 2) i = 2;
00093 kepler_tolerance_level = i;
00094 }
00095
00096 void set_kepler_print_trig_warning(bool p)
00097 {
00098 print_trig_warning = p;
00099 }
00100
00101 int kepler_tolerance()
00102 {
00103 return kepler_tolerance_level;
00104 }
00105
00106 local int check_trig_limit(kepler* k, real &c, char *s)
00107 {
00108 int forced = 0;
00109
00110 if (abs(c) > 1) {
00111 if (kepler_tolerance_level == 0) {
00112 cerr.precision(HIGH_PRECISION);
00113 cerr << s << ": c = " << c << endl;
00114 k->print_all(cerr);
00115 err_exit("cosine out of range");
00116 } else if (kepler_tolerance_level == 1) {
00117 if (c > 1) {
00118 if (c > 1 + TRIG_TOL) {
00119 cerr.precision(HIGH_PRECISION);
00120 cerr << s << ": c = " << c << endl;
00121 k->print_all(cerr);
00122 err_exit("cosine > 1");
00123 }
00124 c = 1;
00125 } else if (c < -1) {
00126 if (c < -1 - TRIG_TOL) {
00127 cerr.precision(HIGH_PRECISION);
00128 cerr << s << ": c = " << c << endl;
00129 k->print_all(cerr);
00130 err_exit("cosine < -1");
00131 }
00132 c = -1;
00133 }
00134 } else {
00135 if (print_trig_warning) {
00136 int p = cerr.precision(HIGH_PRECISION);
00137 cerr << "warning: " << s << ": c = " << c << endl;
00138 cerr.precision(p);
00139 }
00140 c = max(-1.0, min(1.0, c));
00141 }
00142
00143 forced = 1;
00144 }
00145
00146 return forced;
00147 }
00148
00149 local void err_or_warn(char *s)
00150 {
00151 if (kepler_tolerance_level > 0)
00152 warning(s);
00153 else
00154 err_exit(s);
00155 }
00156
00157
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176 local int keplerseq(real mean_an,
00177 real ecc,
00178 real &true_an,
00179 real &ecc_an)
00180 {
00181 if (ecc < 0) {
00182 cerr << "keplerseq: eccentricity e = " << ecc << " < 0\n";
00183 exit (1);
00184 }
00185
00186 if (ecc > 1) {
00187 cerr << "keplerseq: eccentricity e = " << ecc << " > 1\n";
00188 exit (1);
00189 }
00190
00191 mean_an = sym_angle( mean_an );
00192 int iter = 0;
00193
00194
00195 if (mean_an == 0)
00196
00197 ecc_an = 0;
00198
00199 else {
00200
00201 real delta_ecc_an;
00202 real function;
00203 real derivative;
00204
00205 ecc_an = mean_an;
00206 delta_ecc_an = 1;
00207
00208 int counter = 2;
00209
00210 while (counter > 0) {
00211
00212 if (abs( delta_ecc_an ) < ITERAC ) counter--;
00213
00214 if (++iter > MAXITER)
00215 err_or_warn("keplerseq: convergence too slow");
00216
00217 function = -mean_an + ecc_an - ecc * sin(ecc_an);
00218
00219 derivative = 1 - ecc * cos(ecc_an);
00220
00221 delta_ecc_an = -function / derivative;
00222
00223
00224 if ( delta_ecc_an > 1 )
00225 delta_ecc_an = 1;
00226 else if ( delta_ecc_an < -1 )
00227 delta_ecc_an = -1;
00228
00229 ecc_an += delta_ecc_an;
00230 }
00231
00232
00233 }
00234
00235
00236
00237 if (ecc < 1)
00238 true_an = 2 * atan( sqrt((1 + ecc)/(1 - ecc)) * tan(ecc_an/2) );
00239 else
00240 true_an = ecc_an;
00241
00242 return iter;
00243 }
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257
00258
00259 local int keplershypeq(real mean_an,
00260 real ecc,
00261 real &true_an,
00262 real &ecc_an)
00263 {
00264 real delta_ecc_an;
00265 real function;
00266 real derivative;
00267 int i;
00268
00269 if (ecc < 1)
00270 {
00271 cerr << "keplershypeq: eccentricity e = " << ecc << " < 1\n";
00272 exit (1);
00273 }
00274
00275 if (mean_an == 0)
00276
00277 ecc_an = 0;
00278
00279 else {
00280
00281 ecc_an = asinh( mean_an / ecc );
00282
00283 i = 0;
00284 delta_ecc_an = 1;
00285
00286 int counter = 2;
00287
00288 while ( counter > 0) {
00289
00290 if (abs( delta_ecc_an ) < ITERAC ) counter--;
00291
00292 if (++i > MAXITER) {
00293 cerr << "keplershypeq: mean_an = " << mean_an
00294 << " ecc = " << ecc << " MAXITER = " << MAXITER
00295 << endl;
00296 err_or_warn("keplershypeq: convergence too slow");
00297 }
00298
00299 function = -mean_an - ecc_an + ecc * sinh(ecc_an);
00300
00301 derivative = -1 + ecc * cosh(ecc_an);
00302
00303 delta_ecc_an = -function / derivative;
00304
00305
00306 if ( abs(derivative) < 1 ) {
00307 if ( delta_ecc_an > 1 )
00308 delta_ecc_an = 1;
00309 else if ( delta_ecc_an < -1 )
00310 delta_ecc_an = -1;
00311 }
00312 ecc_an += delta_ecc_an;
00313 }
00314 }
00315
00316
00317
00318 if (ecc > 1)
00319 true_an = 2 * atan( sqrt((ecc + 1)/(ecc - 1)) * tanh(ecc_an/2) );
00320 else
00321 true_an = ecc_an;
00322
00323 return i;
00324 }
00325
00326
00327
00328
00329
00330
00331
00332
00333
00334
00335 local real keplerspareq(real mean_an,
00336 real &true_an)
00337
00338
00339
00340 {
00341 if (mean_an == 0) {
00342
00343 true_an = 0;
00344 return 0;
00345
00346 } else {
00347
00348
00349
00350 real s = atan(2/(3*mean_an));
00351 real w = atan(pow(tan(0.5*abs(s)),0.33333333333333333));
00352 if (s < 0) w = -w;
00353 real t = 2/tan(2*w);
00354 true_an = 2*atan(t);
00355
00356 return t;
00357 }
00358 }
00359
00360
00361
00362
00363
00364
00365 kepler::kepler()
00366 {
00367 time = total_mass = energy = angular_momentum = 0;
00368 rel_pos = rel_vel = 0;
00369 normal_unit_vector = 0;
00370 longitudinal_unit_vector = 0;
00371 transverse_unit_vector = 0;
00372 semi_major_axis = eccentricity = 0;
00373 true_anomaly = mean_anomaly = 0;
00374 time_of_periastron_passage = 0;
00375 pred_time = pred_separation = pred_true_anomaly = 0;
00376 pred_rel_pos = pred_rel_vel = 0;
00377 mean_motion = period = periastron = separation = pred_mean_anomaly = 0;
00378 circular_binary_limit = 0;
00379 }
00380
00381 void new_kepler(dyn * com,
00382 real t)
00383 {
00384 kepler * k;
00385 k = new kepler;
00386
00387 com->set_kepler(k);
00388 dyn_to_kepler(com, t);
00389 }
00390
00391 void new_child_kepler(dyn * com,
00392 real t,
00393 real circ_limit)
00394 {
00395 kepler * k;
00396 k = new kepler;
00397
00398 k->set_circular_binary_limit(circ_limit);
00399
00400 com->get_oldest_daughter()->set_kepler(k);
00401 dyn_to_child_kepler(com, t);
00402 }
00403
00404 void dyn_to_kepler(dyn * com,
00405 real t)
00406 {
00407 kepler * k = com->get_kepler();
00408 dyn *d1, *d2;
00409
00410 if (!(d1 = com->get_oldest_daughter()))
00411 err_exit("dyn_to_kepler: no oldest daughter present");
00412 if (!(d2 = d1->get_younger_sister()))
00413 err_exit("dyn_to_kepler: no second daughter present");
00414
00415 k->set_time(t);
00416 k->set_total_mass( d1->get_mass() + d2->get_mass() );
00417 k->set_rel_pos( d2->get_pos() - d1->get_pos() );
00418 k->set_rel_vel( d2->get_vel() - d1->get_vel() );
00419 k->initialize_from_pos_and_vel();
00420 }
00421
00422 void dyn_to_child_kepler(dyn * com,
00423 real t)
00424 {
00425 kepler * k = com->get_oldest_daughter()->get_kepler();
00426 dyn *d1, *d2;
00427
00428 if (!(d1 = com->get_oldest_daughter()))
00429 err_exit("dyn_to_kepler: no oldest daughter present");
00430 if (!(d2 = d1->get_younger_sister()))
00431 err_exit("dyn_to_kepler: no second daughter present");
00432
00433 k->set_time(t);
00434 k->set_total_mass( d1->get_mass() + d2->get_mass() );
00435 k->set_rel_pos( d2->get_pos() - d1->get_pos() );
00436 k->set_rel_vel( d2->get_vel() - d1->get_vel() );
00437 k->initialize_from_pos_and_vel();
00438 }
00439
00440 void kepler::kep_to_dyn_story(story * s)
00441 {
00442 putrq(s, "kepler_time", time);
00443 putrq(s, "kepler_total_mass", total_mass);
00444 putvq(s, "kepler_rel_pos", rel_pos);
00445 putvq(s, "kepler_rel_vel", rel_vel);
00446 putrq(s, "kepler_semi_major_axis", semi_major_axis);
00447 putrq(s, "kepler_eccentricity", eccentricity);
00448 }
00449
00450 void kepler::print_all(ostream & s)
00451 {
00452 int p = s.precision(HIGH_PRECISION);
00453
00454 s << " time = " << time << endl;
00455 s.precision(8);
00456 s << " total_mass = " << total_mass << endl;
00457 s << " rel_pos = " << rel_pos << endl;
00458 s << " rel_vel = " << rel_vel << endl;
00459 s << " separation = " << separation << endl;
00460 s << " true_anomaly = " << (energy < 0 ? sym_angle(true_anomaly) :
00461 true_anomaly) << endl;
00462 s << " mean_anomaly = " << (energy < 0 ? sym_angle(mean_anomaly) :
00463 mean_anomaly) << endl;
00464 s << " semi_major_axis = " << semi_major_axis << endl;
00465 s << " eccentricity = " << eccentricity << endl;
00466 s << " circular_binary_limit = " << circular_binary_limit << endl;
00467 s << " periastron = " << periastron << endl;
00468 s << " apastron = " << (energy >= 0 ? INFINITY :
00469 semi_major_axis * (1 + eccentricity))
00470 << endl;
00471 s << " energy = " << energy << endl;
00472 s << " angular_momentum = " << angular_momentum << endl;
00473 s << " period = " << period << endl;
00474 s << " mean_motion = " << mean_motion << endl;
00475 s << " normal_unit_vector = " << normal_unit_vector << endl;
00476 s << " longitudinal_unit_vector = "
00477 << longitudinal_unit_vector << endl;
00478 s << " transverse_unit_vector = " << transverse_unit_vector << endl;
00479 s << " time_of_periastron_passage = "
00480 << time_of_periastron_passage << endl;
00481
00482 s.precision(p);
00483 }
00484
00485 void kepler::print_dyn(ostream & s)
00486 {
00487 s << " time = " << time << endl;
00488 s << " rel_pos = " << rel_pos << endl;
00489 s << " rel_vel = " << rel_vel << endl;
00490 s << " separation = " << separation << endl;
00491 s << " true_anomaly = " << (energy < 0 ? sym_angle(true_anomaly) :
00492 true_anomaly) << endl;
00493 s << " mean_anomaly = " << (energy < 0 ? sym_angle(mean_anomaly) :
00494 mean_anomaly) << endl;
00495 s << " time_of_periastron_passage = "
00496 << time_of_periastron_passage << endl;
00497 }
00498
00499 void kepler::print_elements(ostream & s)
00500 {
00501 int p = s.precision(8);
00502
00503 s << " time = " << time << endl;
00504 s << " separation = " << separation << endl;
00505 s << " semi_major_axis = " << semi_major_axis << endl;
00506 s << " eccentricity = " << eccentricity << endl;
00507 s << " circular_binary_limit = " << circular_binary_limit << endl;
00508 s << " periastron = " << periastron << endl;
00509 s << " apastron = " << (energy >= 0 ? INFINITY :
00510 semi_major_axis * (1 + eccentricity))
00511 << endl;
00512 s << " energy = " << energy << endl;
00513 s << " angular_momentum = " << angular_momentum << endl;
00514 s << " period = " << period << endl;
00515
00516 s.precision(p);
00517 }
00518
00519
00520
00521
00522
00523
00524
00525
00526
00527
00528
00529
00530
00531
00532
00533
00534
00535
00536
00537
00538
00539
00540
00541 void kepler::pred_true_to_mean_anomaly()
00542 {
00543 real ecc_anomaly;
00544
00545 if (energy < 0) {
00546
00547 if (eccentricity < 1) {
00548 ecc_anomaly = acos((eccentricity + cos(pred_true_anomaly)) /
00549 (1 + eccentricity*cos(pred_true_anomaly)));
00550 if (pred_true_anomaly < 0) ecc_anomaly = -ecc_anomaly;
00551
00552 } else
00553
00554 ecc_anomaly = pred_true_anomaly;
00555
00556 pred_mean_anomaly = ecc_anomaly - eccentricity * sin(ecc_anomaly);
00557
00558 } else if (energy > 0) {
00559
00560 if (eccentricity > 1) {
00561
00562
00563
00564 real maxtruean = PI - acos( 1 / eccentricity );
00565
00566 if (pred_true_anomaly < -maxtruean) {
00567 cerr << "pred_true_to_mean_anomaly: hyp. true anom. = "
00568 << pred_true_anomaly << " < minimum = " << -maxtruean <<"\n";
00569 exit (1);
00570 }
00571
00572 if (pred_true_anomaly > maxtruean) {
00573 cerr << "pred_true_to_mean_anomaly: hyp. true anom. = "
00574 << pred_true_anomaly << " > maximum = " << maxtruean << "\n";
00575 exit (1);
00576 }
00577
00578 ecc_anomaly = acosh((eccentricity + cos(pred_true_anomaly)) /
00579 (1 + eccentricity * cos(pred_true_anomaly)));
00580 if (pred_true_anomaly < 0) ecc_anomaly = -ecc_anomaly;
00581
00582 } else
00583
00584 ecc_anomaly = pred_true_anomaly;
00585
00586 pred_mean_anomaly = -ecc_anomaly + eccentricity * sinh(ecc_anomaly);
00587
00588 } else {
00589
00590
00591
00592
00593 if (angular_momentum > 0) {
00594
00595 real tan_half_true_an = tan(0.5*pred_true_anomaly);
00596 pred_mean_anomaly = tan_half_true_an
00597 *(1 + tan_half_true_an*tan_half_true_an/3);
00598
00599 } else
00600
00601 pred_mean_anomaly = pred_true_anomaly;
00602
00603 }
00604 }
00605
00606
00607
00608
00609
00610
00611
00612
00613
00614
00615
00616
00617
00618
00619
00620
00621
00622
00623
00624
00625
00626
00627 void kepler::mean_anomaly_to_periastron_passage()
00628 {
00629
00630
00631
00632
00633 time_of_periastron_passage = time - mean_anomaly / mean_motion;
00634 }
00635
00636 void kepler::update_time_of_periastron()
00637 {
00638
00639 if (energy >= 0) return;
00640
00641 real dt = (time - time_of_periastron_passage) / period;
00642
00643 int dt_int = (int) abs(dt);
00644 if (dt < 0.0) dt_int = -dt_int - 1;
00645
00646 time_of_periastron_passage += dt_int * period;
00647 }
00648
00649 void kepler::set_real_from_pred()
00650 {
00651 time = pred_time;
00652 rel_pos = pred_rel_pos;
00653 rel_vel = pred_rel_vel;
00654 separation = pred_separation;
00655 true_anomaly = pred_true_anomaly;
00656 mean_anomaly = pred_mean_anomaly;
00657 update_time_of_periastron();
00658 }
00659
00660 void kepler::to_pred_rel_pos_vel(real cos_true_an, real sin_true_an)
00661 {
00662
00663 vector r_unit = cos_true_an * longitudinal_unit_vector +
00664 sin_true_an * transverse_unit_vector;
00665 pred_rel_pos = pred_separation * r_unit;
00666
00667 real rel_vel_squared = 2 * (energy + total_mass / pred_separation);
00668 real v_t = angular_momentum / pred_separation;
00669 real v_r = sqrt(max(0.0, rel_vel_squared - v_t * v_t));
00670
00671
00672 if (sin_true_an < 0) v_r = -v_r;
00673
00674 pred_rel_vel = v_r * r_unit
00675 + v_t * (-sin_true_an * longitudinal_unit_vector
00676 + cos_true_an * transverse_unit_vector);
00677 }
00678
00679 void kepler::to_pred_rel_pos_vel_linear(real true_an)
00680 {
00681 pred_rel_pos = -pred_separation * longitudinal_unit_vector;
00682
00683 if (pred_separation > 0) {
00684 real v_r = sqrt(max(0.0, 2 * (energy + total_mass / pred_separation)));
00685 if (true_an < 0) v_r = -v_r;
00686 pred_rel_vel = -v_r * longitudinal_unit_vector;
00687 } else {
00688 warning("to_pred_rel_pos_vel_linear: r <= 0");
00689 pred_rel_vel = VERY_LARGE_NUMBER*transverse_unit_vector;
00690 }
00691 }
00692
00693
00694
00695
00696
00697 static bool kepler_fast_flag = false;
00698 static real kepler_fast_tol = 1.0e-6;
00699
00700 void set_kepler_fast_flag(bool flag)
00701 {kepler_fast_flag = flag;}
00702 void clear_kepler_fast_flag() {kepler_fast_flag = false;}
00703 bool get_kepler_fast_flag() {return kepler_fast_flag;}
00704
00705 void set_kepler_fast_tol(real tol)
00706 {kepler_fast_tol = tol;}
00707 void reset_kepler_fast_tol() {kepler_fast_tol = 1.e-6;}
00708 real get_kepler_fast_tol() {return kepler_fast_tol;}
00709
00710 local inline void fast_keplerseq(real M, real e, real tol,
00711 real &s, real &c)
00712 {
00713
00714
00715
00716 M = sym_angle(M);
00717
00718
00719
00720 real dE = 0.94*e;
00721 if (M < 0) dE = -dE;
00722
00723 real E = M + dE;
00724
00725 while (1) {
00726
00727 s = sin(E);
00728
00729
00730
00731
00732 c = sqrt(1 - s*s);
00733 if (fabs(E) > M_PI/2) c = -c;
00734
00735 real func = E - e*s - M;
00736 if (fabs(func) <= tol) return;
00737
00738 E -= func/(1 - e*c);
00739 }
00740 }
00741
00742
00743
00744 void kepler::fast_to_pred_rel_pos_vel(real r_cos_true_an,
00745 real r_sin_true_an)
00746 {
00747
00748
00749 pred_rel_pos = r_cos_true_an * longitudinal_unit_vector +
00750 r_sin_true_an * transverse_unit_vector;
00751
00752
00753
00754 real ri = 1 / pred_separation;
00755 vector r_unit = ri * pred_rel_pos;
00756
00757 real rel_vel_squared = 2 * (energy + total_mass * ri);
00758 real v_t = angular_momentum * ri;
00759 real v_r = sqrt(max(0.0, rel_vel_squared - v_t * v_t));
00760
00761
00762 if (r_sin_true_an < 0) v_r = -v_r;
00763
00764 pred_rel_vel = v_r * r_unit
00765 + v_t * ri * (-r_sin_true_an * longitudinal_unit_vector
00766 + r_cos_true_an * transverse_unit_vector);
00767 }
00768
00769 void kepler::fast_pred_mean_anomaly_to_pos_and_vel()
00770 {
00771 real cos_ecc_an, sin_ecc_an;
00772 fast_keplerseq(pred_mean_anomaly, eccentricity, kepler_fast_tol,
00773 sin_ecc_an, cos_ecc_an);
00774
00775 pred_separation = semi_major_axis * (1 - eccentricity * cos_ecc_an);
00776 real r_cos_true_an = semi_major_axis * (cos_ecc_an - eccentricity);
00777
00778
00779
00780 real semi_minor_axis = angular_momentum * period / (2*M_PI*semi_major_axis);
00781 real r_sin_true_an = semi_minor_axis * sin_ecc_an;
00782
00783 fast_to_pred_rel_pos_vel(r_cos_true_an, r_sin_true_an);
00784 }
00785
00786
00787
00788
00789
00790 void kepler::pred_mean_anomaly_to_pos_and_vel()
00791 {
00792 if (!kepler_fast_flag || energy >= 0 || angular_momentum == 0) {
00793
00794
00795
00796 real ecc_an;
00797
00798
00799
00800 if (energy != 0) {
00801
00802 if (energy < 0) {
00803
00804 keplerseq(pred_mean_anomaly, eccentricity,
00805 pred_true_anomaly, ecc_an);
00806
00807 pred_separation = semi_major_axis
00808 * (1 - eccentricity * cos(ecc_an));
00809
00810 } else {
00811
00812 keplershypeq(pred_mean_anomaly, eccentricity,
00813 pred_true_anomaly, ecc_an);
00814
00815 pred_separation = semi_major_axis
00816 * (eccentricity * cosh(ecc_an) - 1);
00817 }
00818
00819
00820
00821
00822
00823
00824 } else {
00825
00826 if (angular_momentum > 0) {
00827
00828 real tan_half_true_an =
00829 keplerspareq(pred_mean_anomaly, pred_true_anomaly);
00830
00831 pred_separation = periastron
00832 * (1 + tan_half_true_an*tan_half_true_an);
00833
00834 } else {
00835
00836 pred_true_anomaly = pred_mean_anomaly;
00837 pred_separation = pow(abs(pred_true_anomaly), 2/3.0);
00838
00839 }
00840 }
00841
00842
00843
00844 if (angular_momentum > 0)
00845 to_pred_rel_pos_vel(cos(pred_true_anomaly), sin(pred_true_anomaly));
00846 else
00847 to_pred_rel_pos_vel_linear(pred_true_anomaly);
00848
00849 } else {
00850
00851
00852
00853
00854 fast_pred_mean_anomaly_to_pos_and_vel();
00855 }
00856 }
00857
00858
00859
00860 #if 0
00861
00862
00863
00864 void kepler::pred_mean_anomaly_to_pos_and_vel()
00865 {
00866 real ecc_an;
00867
00868
00869
00870 if (energy != 0) {
00871
00872 if (energy < 0) {
00873
00874 keplerseq(pred_mean_anomaly, eccentricity,
00875 pred_true_anomaly, ecc_an);
00876
00877
00878 pred_separation = semi_major_axis
00879 * (1 - eccentricity * cos(ecc_an));
00880
00881 } else {
00882
00883 keplershypeq(pred_mean_anomaly, eccentricity,
00884 pred_true_anomaly, ecc_an);
00885
00886 pred_separation = semi_major_axis
00887 * (eccentricity * cosh(ecc_an) - 1);
00888 }
00889
00890
00891
00892
00893
00894
00895 } else {
00896
00897 if (angular_momentum > 0) {
00898
00899 real tan_half_true_an =
00900 keplerspareq(pred_mean_anomaly, pred_true_anomaly);
00901
00902 pred_separation = periastron
00903 * (1 + tan_half_true_an*tan_half_true_an);
00904
00905 } else {
00906
00907 pred_true_anomaly = pred_mean_anomaly;
00908 pred_separation = pow(abs(pred_true_anomaly), 0.6666666666666667);
00909
00910 }
00911 }
00912
00913
00914
00915 if (angular_momentum > 0)
00916 to_pred_rel_pos_vel(cos(pred_true_anomaly), sin(pred_true_anomaly));
00917 else
00918 to_pred_rel_pos_vel_linear(pred_true_anomaly);
00919 }
00920
00921 #endif
00922
00923
00924
00925
00926
00927
00928
00929
00930
00931
00932 void kepler::align_with_axes(int axis)
00933 {
00934 if (axis == 1) {
00935
00936 longitudinal_unit_vector = vector(1, 0, 0);
00937 transverse_unit_vector = vector(0, 1, 0);
00938
00939 } else if (axis == 2) {
00940
00941 longitudinal_unit_vector = vector(0, 1, 0);
00942 transverse_unit_vector = vector(0, 0, 1);
00943
00944 } else {
00945
00946 longitudinal_unit_vector = vector(0, 0, 1);
00947 transverse_unit_vector = vector(1, 0, 0);
00948 }
00949
00950 normal_unit_vector = longitudinal_unit_vector ^ transverse_unit_vector;
00951
00952 }
00953
00954
00955
00956
00957 void kepler::initialize_from_pos_and_vel(bool minimal, bool verbose)
00958 {
00959
00960
00961
00962 energy = 0.5 * rel_vel * rel_vel - total_mass / separation;
00963
00964 normal_unit_vector = rel_pos ^ rel_vel;
00965 angular_momentum = abs(normal_unit_vector);
00966
00967 if (energy != 0) {
00968
00969 semi_major_axis = -0.5 * total_mass / energy;
00970
00971 real rdotv = rel_pos * rel_vel;
00972 real temp = 1 - separation / semi_major_axis;
00973 eccentricity = sqrt(rdotv * rdotv / (total_mass * semi_major_axis)
00974 + temp * temp);
00975
00976
00977
00978 if (eccentricity < 1.e-14) eccentricity = 0;
00979
00980 if ((energy > 0 && eccentricity < 1
00981 && eccentricity > 1 - SQRT_TRIG_TOL)
00982 || (angular_momentum > 0 && eccentricity == 1)) {
00983
00984
00985
00986 eccentricity = 1;
00987 angular_momentum = 0;
00988
00989 } else if (eccentricity > 0 &&
00990 eccentricity < circular_binary_limit) {
00991
00992
00993
00994
00995
00996
00997
00998
00999
01000
01001
01002
01003 if (verbose && (print_trig_warning
01004 || eccentricity > 0.1*circular_binary_limit))
01005 cerr << "kepler: binary with ecc = " << eccentricity
01006 << " forced to be circular" << endl
01007 << " in kepler::initialize_from_pos_and_vel"
01008 << endl;
01009
01010 eccentricity = 0;
01011
01012
01013
01014
01015
01016
01017
01018 }
01019
01020
01021
01022 semi_major_axis = abs(semi_major_axis);
01023 periastron = semi_major_axis * abs(1 - eccentricity);
01024
01025 mean_motion = sqrt(total_mass /
01026 (semi_major_axis*semi_major_axis*semi_major_axis));
01027 period = TWO_PI / mean_motion;
01028
01029 } else {
01030
01031 eccentricity = 1;
01032
01033 semi_major_axis = INFINITY;
01034 periastron = 0.5 * angular_momentum * angular_momentum / total_mass;
01035
01036 mean_motion = sqrt(total_mass /
01037 (2 * periastron * periastron * periastron));
01038
01039
01040
01041
01042 period = INFINITY;
01043 }
01044
01045 if (angular_momentum != 0)
01046 normal_unit_vector /= angular_momentum;
01047 else {
01048 eccentricity = 1;
01049 periastron = 0;
01050 if (energy == 0) mean_motion = sqrt(4.5*total_mass);
01051 }
01052
01053
01054 if (minimal) return;
01055
01056
01057
01058
01059
01060
01061 vector r_unit = rel_pos / separation;
01062
01063 if (angular_momentum == 0) {
01064 vector temp = vector(1,0,0);
01065 if (abs(r_unit[0]) > .5) temp = vector(0,1,0);
01066 normal_unit_vector = r_unit ^ temp;
01067 normal_unit_vector /= abs(normal_unit_vector);
01068 }
01069
01070 vector t_unit = normal_unit_vector ^ r_unit;
01071
01072 real cos_true_an = 1, sin_true_an = 0;
01073
01074
01075
01076 if (eccentricity > 0)
01077 if (eccentricity != 1) {
01078
01079
01080
01081
01082
01083
01084
01085
01086
01087
01088
01089 real tmp = ((periastron/separation) * (1 + eccentricity) - 1)
01090 / eccentricity;
01091 cos_true_an = tmp;
01092
01093
01094
01095
01096
01097
01098
01099
01100 if (fabs(cos_true_an) > 1.0) {
01101
01102
01103
01104 }
01105
01106 check_trig_limit(this, cos_true_an, "set_phase #1");
01107
01108 sin_true_an = cos_to_sin(cos_true_an);
01109 if (rel_pos * rel_vel < 0) sin_true_an = -sin_true_an;
01110
01111 if (1 - cos_true_an < TRIG_TOL) {
01112 true_anomaly = 0;
01113 cos_true_an = 1;
01114 sin_true_an = 0;
01115 } else
01116 true_anomaly = acos(cos_true_an);
01117
01118 if (sin_true_an < 0) true_anomaly = -true_anomaly;
01119
01120 } else {
01121
01122 if (angular_momentum > 0) {
01123
01124
01125
01126 true_anomaly = 2*acos(sqrt(periastron/separation));
01127 if (rel_pos * rel_vel < 0) true_anomaly = -true_anomaly;
01128
01129 cos_true_an = cos(true_anomaly);
01130 sin_true_an = sin(true_anomaly);
01131
01132 } else {
01133
01134 if (energy < 0) {
01135
01136 cos_true_an = 1 - separation / semi_major_axis;
01137 check_trig_limit(this, cos_true_an, "set_phase #2");
01138 true_anomaly = acos(cos_true_an);
01139
01140 } else if (energy > 0)
01141
01142 true_anomaly = acosh(1 + separation / semi_major_axis);
01143
01144 else
01145
01146 true_anomaly = pow(separation, 1.5);
01147
01148 if (rel_pos * rel_vel < 0) true_anomaly = -true_anomaly;
01149
01150 cos_true_an = -1;
01151 sin_true_an = 0;
01152 }
01153 }
01154
01155 longitudinal_unit_vector = cos_true_an * r_unit - sin_true_an * t_unit;
01156 transverse_unit_vector = sin_true_an * r_unit + cos_true_an * t_unit;
01157
01158 pred_true_anomaly = true_anomaly;
01159 pred_true_to_mean_anomaly();
01160 mean_anomaly = pred_mean_anomaly;
01161
01162 mean_anomaly_to_periastron_passage();
01163 }
01164
01165
01166
01167
01168
01169
01170
01171
01172
01173
01174
01175
01176
01177 void kepler::initialize_from_shape_and_phase()
01178 {
01179 if (eccentricity != 1) {
01180
01181 energy = .5 * total_mass / semi_major_axis;
01182 if (eccentricity < 1) energy = -energy;
01183
01184 periastron = semi_major_axis * abs(1 - eccentricity);
01185 mean_motion = sqrt(total_mass
01186 /(semi_major_axis*semi_major_axis*semi_major_axis));
01187 period = (energy < 0 ? TWO_PI / mean_motion : INFINITY);
01188
01189 angular_momentum = sqrt(abs(1 - eccentricity * eccentricity)
01190 * total_mass * semi_major_axis);
01191
01192 } else if (periastron == 0) {
01193
01194 angular_momentum = 0;
01195
01196 if (energy == 0) {
01197
01198 period = semi_major_axis = INFINITY;
01199 mean_motion = sqrt(4.5*total_mass);
01200
01201 } else {
01202
01203 energy = sign(energy) * .5 * total_mass / semi_major_axis;
01204
01205 mean_motion = sqrt(total_mass/(semi_major_axis
01206 *semi_major_axis*semi_major_axis));
01207 period = (energy < 0 ? TWO_PI / mean_motion : INFINITY);
01208 }
01209
01210 } else {
01211
01212 energy = 0;
01213 period = semi_major_axis = INFINITY;
01214
01215 mean_motion = sqrt(total_mass
01216 / (2*periastron*periastron*periastron));
01217
01218 angular_momentum = sqrt(2 * total_mass * periastron);
01219 }
01220
01221 mean_anomaly_to_periastron_passage();
01222 pred_time = time;
01223 pred_mean_anomaly = mean_anomaly;
01224
01225 pred_mean_anomaly_to_pos_and_vel();
01226 set_real_from_pred();
01227 }
01228
01229
01230
01231
01232
01233
01234 real kepler::pred_advance_to_periastron() {
01235
01236 if (eccentricity >= 1 && true_anomaly > 0)
01237 err_or_warn("pred_advance_to_periastron: outgoing hyperbolic orbit");
01238
01239 pred_time = time_of_periastron_passage;
01240
01241 if (eccentricity < 1) {
01242
01243
01244
01245
01246
01247
01248
01249
01250
01251 real n_orbits;
01252 if (pred_time - time < 0.0) {
01253
01254 real n_orbits = ceil((time-pred_time)/(TWO_PI/mean_motion));
01255 pred_time += n_orbits * (TWO_PI/mean_motion);
01256 }
01257 if (pred_time < time) {
01258 cerr << "kepler::pred_advance_to_periastron(): shouldn't happen!"
01259 << endl;
01260 PRC(pred_time); PRC(time); PRC(n_orbits); PRL(pred_time-time);
01261 }
01262 }
01263
01264 pred_true_anomaly = 0;
01265 pred_mean_anomaly = 0;
01266
01267 pred_separation = periastron;
01268
01269 to_pred_rel_pos_vel(1, 0);
01270
01271 return pred_time;
01272 }
01273
01274 real kepler::pred_return_to_periastron()
01275 {
01276 if (eccentricity >= 1 && true_anomaly < 0)
01277 err_or_warn("pred_return_to_periastron: incoming hyperbolic orbit");
01278
01279 pred_time = time_of_periastron_passage;
01280 pred_true_anomaly = 0;
01281 pred_mean_anomaly = 0;
01282
01283 pred_separation = periastron;
01284
01285 to_pred_rel_pos_vel(1, 0);
01286
01287 return pred_time;
01288 }
01289
01290 real kepler::pred_advance_to_apastron()
01291 {
01292 if (eccentricity >= 1)
01293 err_or_warn("pred_advance_to_apastron: hyperbolic orbit");
01294
01295 if (true_anomaly < 0)
01296 pred_time = time_of_periastron_passage + 1.5 * TWO_PI / mean_motion;
01297 else
01298 pred_time = time_of_periastron_passage + PI / mean_motion;
01299
01300 pred_true_anomaly = PI;
01301 pred_mean_anomaly = PI;
01302 pred_separation = semi_major_axis * (1 + eccentricity);
01303 to_pred_rel_pos_vel(-1, 0);
01304
01305 return pred_time;
01306 }
01307
01308 real kepler::pred_return_to_apastron()
01309 {
01310 if (eccentricity >= 1)
01311 err_or_warn("pred_return_to_apastron: hyperbolic orbit");
01312
01313 if (true_anomaly < 0)
01314 pred_time = time_of_periastron_passage + PI / mean_motion;
01315 else
01316 pred_time = time_of_periastron_passage - PI / mean_motion;
01317
01318 pred_true_anomaly = PI;
01319 pred_mean_anomaly = PI;
01320 pred_separation = semi_major_axis * (1 + eccentricity);
01321 to_pred_rel_pos_vel(-1, 0);
01322
01323 return pred_time;
01324 }
01325
01326 real kepler::pred_transform_to_radius(real r, int direction)
01327 {
01328
01329
01330 if (energy < 0)
01331 true_anomaly = sym_angle(true_anomaly);
01332 else if (separation > r && direction*true_anomaly > 0) {
01333 err_or_warn(
01334 "pred_transform_to_radius: unbound orbit inside target radius");
01335 if (direction > 0)
01336 cerr << "Mapping to incoming branch." << endl;
01337 else
01338 cerr << "Mapping to outgoing branch." << endl;
01339 }
01340
01341 if (r < periastron) {
01342 if (kepler_tolerance_level <= 1)
01343 err_exit("pred_transform_to_radius: r < periastron");
01344 r = periastron;
01345 }
01346
01347 pred_separation = r;
01348 real cos_true_an, sin_true_an;
01349
01350
01351
01352 if (eccentricity != 1) {
01353
01354
01355
01356
01357
01358 if (eccentricity > 0 && r > 0)
01359
01360 cos_true_an = ((periastron/r) * (1 + eccentricity) - 1)
01361 / eccentricity;
01362 else
01363 cos_true_an = 2;
01364
01365 if (check_trig_limit(this, cos_true_an,
01366 "pred_transform_to_radius #1")) {
01367
01368
01369
01370
01371 if (cos_true_an > 0)
01372 return (direction == 1 ? pred_advance_to_periastron()
01373 : pred_return_to_periastron());
01374 else
01375 return (direction == 1 ? pred_advance_to_apastron()
01376 : pred_return_to_apastron());
01377 }
01378
01379 pred_true_anomaly = acos(cos_true_an);
01380 sin_true_an = cos_to_sin(cos_true_an);
01381
01382 } else {
01383
01384
01385
01386 if (angular_momentum > 0) {
01387
01388 pred_true_anomaly = 2*acos(sqrt(min(1.0, periastron/r)));
01389 cos_true_an = cos(pred_true_anomaly);
01390 sin_true_an = sin(pred_true_anomaly);
01391
01392 } else {
01393
01394 if (energy < 0) {
01395
01396 cos_true_an = 1 - r / semi_major_axis;
01397 check_trig_limit(this, cos_true_an,
01398 "pred_transform_to_radius #2");
01399 pred_true_anomaly = acos(cos_true_an);
01400
01401 } else if (energy > 0)
01402
01403 pred_true_anomaly = acosh(1 + r / semi_major_axis);
01404
01405 else
01406
01407 pred_true_anomaly = pow(r, 1.5);
01408 }
01409 }
01410
01411
01412
01413 if ( direction * (r - separation) < 0 ) {
01414 pred_true_anomaly = -pred_true_anomaly;
01415 if (angular_momentum > 0) sin_true_an = -sin_true_an;
01416 }
01417
01418
01419
01420 if (angular_momentum > 0)
01421 to_pred_rel_pos_vel(cos_true_an, sin_true_an);
01422 else
01423 to_pred_rel_pos_vel_linear(pred_true_anomaly);
01424
01425
01426
01427 pred_true_to_mean_anomaly();
01428
01429
01430
01431 if (energy < 0)
01432 while ( direction * (pred_mean_anomaly - mean_anomaly) > TWO_PI )
01433 pred_mean_anomaly -= direction*TWO_PI;
01434
01435
01436
01437
01438
01439 pred_time = time + (pred_mean_anomaly - mean_anomaly) / mean_motion;
01440 while (direction*(pred_time-time) < 0) pred_time += direction*period;
01441
01442 return pred_time;
01443 }
01444
01445 real kepler::pred_advance_to_radius(real r)
01446 {
01447 return pred_transform_to_radius(r, +1);
01448 }
01449
01450 real kepler::pred_return_to_radius(real r)
01451 {
01452 return pred_transform_to_radius(r, -1);
01453 }
01454
01455 real kepler::pred_transform_to_time(real t)
01456 {
01457 pred_mean_anomaly = mean_anomaly + mean_motion * (t - time);
01458
01459 pred_time = t;
01460
01461 pred_mean_anomaly_to_pos_and_vel();
01462 return pred_separation;
01463 }
01464
01465 real kepler::advance_to_radius(real r)
01466 {
01467 time = pred_advance_to_radius(r);
01468 set_real_from_pred();
01469
01470 return time;
01471 }
01472
01473 real kepler::return_to_radius(real r)
01474 {
01475 time = pred_return_to_radius(r);
01476 set_real_from_pred();
01477
01478 return time;
01479 }
01480
01481 real kepler::advance_to_periastron()
01482 {
01483 time = pred_advance_to_periastron();
01484 set_real_from_pred();
01485
01486 return time;
01487 }
01488
01489 real kepler::return_to_periastron()
01490 {
01491 time = pred_return_to_periastron();
01492 set_real_from_pred();
01493
01494 return time;
01495 }
01496
01497 real kepler::advance_to_apastron()
01498 {
01499 time = pred_advance_to_apastron();
01500 set_real_from_pred();
01501
01502 return time;
01503 }
01504
01505 real kepler::return_to_apastron()
01506 {
01507 time = pred_return_to_apastron();
01508 set_real_from_pred();
01509
01510 return time;
01511 }
01512
01513 real kepler::transform_to_time(real t)
01514 {
01515 separation = pred_transform_to_time(t);
01516 set_real_from_pred();
01517
01518 return separation;
01519 }
01520
01521
01522
01523
01524
01525
01526
01527
01528 void make_standard_kepler(kepler &k, real t, real mass,
01529 real energy, real eccentricity,
01530 real q, real mean_anomaly,
01531 int align_axis)
01532 {
01533
01534
01535
01536
01537
01538
01539
01540
01541 k.set_time(t);
01542 k.set_total_mass(mass);
01543 k.set_semi_major_axis((energy == 0 ? VERY_LARGE_NUMBER
01544 : .5*mass/abs(energy)));
01545 k.set_eccentricity(eccentricity);
01546 k.set_periastron(q);
01547 k.set_energy(energy);
01548 k.set_mean_anomaly(mean_anomaly);
01549 if (align_axis >= 1 && align_axis <= 3)
01550 k.align_with_axes(align_axis);
01551
01552 k.initialize_from_shape_and_phase();
01553 }
01554
01555
01556
01557 void set_random_orientation(kepler &k, int planar)
01558 {
01559 real cos_theta = (real) planar, sin_theta = 0;
01560
01561 if (planar == 0) {
01562 cos_theta = randinter(-1, 1);
01563 sin_theta = sqrt(1 - cos_theta * cos_theta);
01564 }
01565
01566 real phi = randinter(0, TWO_PI);
01567 real psi = randinter(0, TWO_PI);
01568
01569
01570
01571 vector n = vector(sin_theta*cos(phi), sin_theta*sin(phi), cos_theta);
01572
01573
01574
01575 vector temp = vector(1, 0, 0);
01576 if (abs(n[0]) > .5) temp = vector(0, 1, 0);
01577 if (n[2] < 0) temp = -temp;
01578
01579 vector b = n ^ temp;
01580 b /= abs(b);
01581 vector a = b ^ n;
01582 if (n[2] < 0) a = -a;
01583
01584
01585
01586
01587 vector l = cos(psi)*a + sin(psi)*b;
01588 vector t = n ^ l;
01589
01590 k.set_orientation(l, t, n);
01591 k.initialize_from_shape_and_phase();
01592 }
01593
01594 #else
01595
01596 kepler k;
01597
01598 local void tt(real t)
01599 {
01600 cerr << " transform_to_time " << t << endl << endl;
01601 k.transform_to_time(t);
01602 k.print_dyn();
01603 }
01604
01605 local void ar(real r)
01606 {
01607 cerr << " advance_to_radius " << r << endl << endl;
01608 k.advance_to_radius(r);
01609 k.print_dyn();
01610 }
01611
01612 local void rr(real r)
01613 {
01614 cerr << " return_to_radius " << r << endl << endl;
01615 k.return_to_radius(r);
01616 k.print_dyn();
01617 }
01618
01619 main(int argc, char **argv)
01620 {
01621 check_help();
01622
01623 bool orbit_params = true;
01624 real time = 0;
01625 real semi = 1;
01626 real ecc = 0;
01627 real mass = 1;
01628 real mean_anomaly = 0;
01629 vector r, v = vector(0);
01630
01631 int i = 0;
01632 while (++i < argc)
01633 if (argv[i][0] == '-')
01634 switch (argv[i][1]) {
01635
01636 case 'a': semi = atof(argv[++i]);
01637 orbit_params = true;
01638 break;
01639 case 'e': ecc = atof(argv[++i]);
01640 break;
01641 case 'm': mass = atof(argv[++i]);
01642 break;
01643 case 'M': mean_anomaly = atof(argv[++i]);
01644 break;
01645 case 't': time = atof(argv[++i]);
01646 break;
01647 case 'x': for (int k = 0; k < 3; k++)
01648 r[0] = atof(argv[++i]);
01649 orbit_params = false;
01650 break;
01651 case 'v': for (int k = 0; k < 3; k++)
01652 v[0] = atof(argv[++i]);
01653 break;
01654 default: cerr << "unknown option \"" << argv[i] << endl;
01655 exit(1);
01656 }
01657
01658 set_kepler_tolerance(2);
01659 cout.precision(12);
01660 cerr.precision(12);
01661
01662 k.set_time(time);
01663 k.set_total_mass(mass);
01664
01665 if (orbit_params) {
01666
01667 k.set_semi_major_axis(semi);
01668 k.set_eccentricity(ecc);
01669 k.set_mean_anomaly(mean_anomaly);
01670 k.align_with_axes(1);
01671 k.initialize_from_shape_and_phase();
01672
01673 cerr << "Initialized from shape and phase" << endl;
01674
01675 } else {
01676
01677 k.set_rel_pos(r);
01678 k.set_rel_vel(v);
01679 k.initialize_from_pos_and_vel();
01680
01681 cerr << "Initialized from pos and vel" << endl;
01682
01683 }
01684
01685 cerr << endl;
01686 k.print_all();
01687
01688
01689
01690 for (;;) {
01691
01692 char opt;
01693
01694 cout << endl << "? ";
01695 opt = 'q';
01696 cin >> opt;
01697 if (opt == 'q') exit(0);
01698
01699 if (opt == 'p') {
01700
01701 cerr << endl;
01702 k.print_all();
01703
01704 } else if (opt == 'h' || opt == '?') {
01705
01706 cerr << " options: a R advance to radius R" << endl
01707 << " d dT increment time by dT" << endl
01708 << " p print" << endl
01709 << " q quit" << endl
01710 << " r R return to radius R" << endl
01711 << " t T evolve to time T" << endl;
01712
01713 } else {
01714
01715 real x;
01716 cin >> x;
01717
01718 if (opt == 'd')
01719 tt(k.get_time()+x);
01720 else if (opt == 't')
01721 tt(x);
01722 else if (opt == 'a')
01723 ar(x);
01724 else if (opt == 'r')
01725 rr(x);
01726 }
01727 }
01728
01729 }
01730
01731 #endif