Planetary Orbital Evolution due to Tides
Orbital evolution of two objects experiencing tides
testDifferentialEquations.cpp
Go to the documentation of this file.
1 
9 
10 namespace Evolve {
11 
13  const alglib::real_1d_array &eccentricities,
14  const alglib::real_1d_array &expected_semimajor_rate,
15  const alglib::real_1d_array &predicted_semimajor_rate,
16  const alglib::real_1d_array &expected_eccentricity_rate,
17  const alglib::real_1d_array &predicted_eccentricity_rate
18  ) const
19  {
20  std::cout << std::setw(25) << "eccentricity"
21  << std::setw(25) << "semimajor_rate"
22  << std::setw(25) << "expected_semimajor_rate"
23  << std::setw(25) << "eccentricity_rate"
24  << std::setw(25) << "expected_ecc_rate"
25  << std::endl;
26  for(
27  unsigned e_index = 0;
28  e_index < eccentricities.length();
29  ++e_index
30  ) {
31  std::cout << std::setw(25) << eccentricities[e_index]
32  << std::setw(25)
33  << predicted_semimajor_rate[e_index]
34  << std::setw(25)
35  << expected_semimajor_rate[e_index]
36  << std::setw(25)
37  << predicted_eccentricity_rate[e_index]
38  << std::setw(25)
39  << expected_eccentricity_rate[e_index]
40  << std::endl;
41  }
42 
43 
44  }
45 
47  int orbital_frequency_multiplier,
48  int spin_frequency_multiplier,
49  double eccentricity
50  ) const
51  {
52 
53  double e2 = std::pow(eccentricity, 2);
54 
55  if(orbital_frequency_multiplier == 2 && spin_frequency_multiplier == 2)
56  return 1.0 - 5.0 * e2;
57 
58  if(orbital_frequency_multiplier == 1 && spin_frequency_multiplier == 0)
59  return 3.0 / 4.0 * e2;
60 
61  if(orbital_frequency_multiplier == 1 && spin_frequency_multiplier == 2)
62  return 1.0 / 8.0 * e2;
63 
64  if(orbital_frequency_multiplier == 3 && spin_frequency_multiplier == 2)
65  return 147.0 / 8.0 * e2;
66 
67  return 0.0;
68  }
69 
71  int orbital_frequency_multiplier,
72  int spin_frequency_multiplier
73  ) const
74  {
75 
76  if(orbital_frequency_multiplier == 2 && spin_frequency_multiplier == 2)
77  return -1.0;
78 
79  if(orbital_frequency_multiplier == 1 && spin_frequency_multiplier == 0)
80  return 1.5;
81 
82  if(orbital_frequency_multiplier == 1 && spin_frequency_multiplier == 2)
83  return -1.0 / 4.0;
84 
85  if(orbital_frequency_multiplier == 3 && spin_frequency_multiplier == 2)
86  return 49.0 / 4.0;
87 
88  return 0.0;
89  }
90 
92  const alglib::real_1d_array& x,
93  const alglib::real_1d_array& y1,
94  const alglib::real_1d_array& y2,
95  unsigned agreement_order,
96  unsigned max_order,
97  const std::string &description
98  )
99  {
100  alglib::real_1d_array difference;
101  difference.setlength(x.length());
102  double min_difference = Core::Inf,
103  max_difference = -Core::Inf,
104  y_scale = 0;
105  for(unsigned i = 0; i < x.length(); ++i) {
106  difference[i] = y2[i] - y1[i];
107  min_difference = std::min(difference[i], min_difference);
108  max_difference = std::max(difference[i], max_difference);
109  y_scale = std::max(y_scale,
110  std::max(std::abs(y1[i]), std::abs(y2[i])));
111  }
112 
113  if(
114  std::max(std::abs(max_difference), std::abs(min_difference))
115  <
116  1e-13 * y_scale
117  )
118  return;
119  alglib::ae_int_t fit_info;
120  alglib::barycentricinterpolant interp;
121  alglib::polynomialfitreport fit_report;
122  alglib::polynomialfit(x,
123  difference,
124  max_order + 1,
125  fit_info,
126  interp,
127  fit_report);
128 
129  double max_allowed_residual = 1e-12 * (max_difference - min_difference);
130 
131  std::ostringstream message;
132  message << description
133  << " too large residual from fit to difference: "
134  << fit_report.maxerror
135  << " > "
136  << max_allowed_residual;
137 
138  TEST_ASSERT_MSG(fit_report.maxerror <= max_allowed_residual,
139  message.str().c_str());
140 
141  alglib::real_1d_array coefficients;
142 
143  alglib::polynomialbar2pow(interp, coefficients);
144 
145  double max_allowed_term = 0.0,
146  max_abs_x = std::max(std::abs(x[0]), x[x.length() - 1]);
147  for(unsigned power = 0; power < coefficients.length(); ++power)
148  max_allowed_term = std::max(
149  max_allowed_term,
150  std::abs(coefficients[power]) * std::pow(max_abs_x, power)
151  );
152  max_allowed_term *= 1e-8;
153 
154  for(unsigned power = 0; power <= agreement_order; ++power) {
155  double max_abs_term_value = (std::abs(coefficients[power])
156  *
157  std::pow(max_abs_x, power));
158  message.str("");
159  message << description
160  << " x^" << power << " coefficient too large: |"
161  << coefficients[power]
162  << " * "
163  << max_abs_x << "^" << power
164  << "| = "
165  << max_abs_term_value
166  << " > "
167  << max_allowed_term;
168 
169  TEST_ASSERT_MSG(max_abs_term_value <= max_allowed_term,
170  message.str().c_str());
171  }
172  }
173 
175  {
178  }
179 
181  {
182  const double MAX_ECCENTRICITY = 0.1;
183  const unsigned NUM_ECCENTRICITIES = 100;
184  const double ECCENTRICITY_STEP = (
185  MAX_ECCENTRICITY / (NUM_ECCENTRICITIES - 1)
186  );
187 
188  alglib::real_1d_array eccentricities,
189  expected_semimajor_rate,
190  predicted_semimajor_rate,
191  expected_eccentricity_rate,
192  predicted_eccentricity_rate;
193  eccentricities.setlength(NUM_ECCENTRICITIES);
194  expected_semimajor_rate.setlength(NUM_ECCENTRICITIES);
195  predicted_semimajor_rate.setlength(NUM_ECCENTRICITIES);
196  expected_eccentricity_rate.setlength(NUM_ECCENTRICITIES);
197  predicted_eccentricity_rate.setlength(NUM_ECCENTRICITIES);
198 
199  eccentricities[0] = 0.0;
200  for(unsigned e_index = 1; e_index < NUM_ECCENTRICITIES; ++e_index)
201  eccentricities[e_index] = (eccentricities[e_index - 1]
202  +
203  ECCENTRICITY_STEP);
204 
205  std::valarray<double> one_poly(1.0, 1);
206  for(
207  int orbital_frequency_multiplier = 0;
208  orbital_frequency_multiplier <= 4;
209  ++orbital_frequency_multiplier
210  ) {
211  for(
212  int spin_frequency_multiplier = 0;
213  spin_frequency_multiplier <= 4;
214  ++spin_frequency_multiplier
215  ) {
216  SingleTidalTermBody primary(0.0,
217  1.0,
218  orbital_frequency_multiplier,
219  spin_frequency_multiplier,
220  1.0,
221  one_poly,
222  one_poly,
223  one_poly);
224  Planet::Planet secondary(1.0, 1.0);
225 
226  BinarySystem binary(primary, secondary);
227 
228  binary.change_expansion_order(2);
229 
230  std::valarray<double> parameters(0.0, 7);
231  std::valarray<double> evolution_rates(parameters.size());
232  parameters[0] = 10.0;
233  parameters[1] = 0.0;
234  binary.configure(true,
235  0.0,
236  &(parameters[0]),
237  Core::BINARY);
238 
239  double common_rate_factor = (
240  -2.4 * M_PI
241  *
242  (primary.mass() + secondary.mass()) / primary.mass()
243  *
245  *
246  Core::AstroConst::solar_mass * secondary.mass()
247  /
248  std::pow(Core::AstroConst::solar_radius, 3)
250 
251  for(double semimajor = 2.0; semimajor <= 10.0; semimajor += 1.0) {
252 
253  double mean_motion = std::sqrt(
255  primary.mass()
256  +
257  secondary.mass()
258  )
259  /
260  std::pow(semimajor * Core::AstroConst::solar_radius, 3)
261  );
262  double rate_factor = (
263  common_rate_factor
264  /
265  (mean_motion * std::pow(semimajor, 8))
266  );
267  for(
268  double primary_spin_frequency = 0.0;
269  primary_spin_frequency <= 5.0 * mean_motion;
270  primary_spin_frequency += 0.2 * mean_motion
271  ) {
272 
273  for(
274  unsigned e_index = 0;
275  e_index < NUM_ECCENTRICITIES;
276  ++e_index
277  )
278  {
279  double age = 1.0,
280  test_e = eccentricities[e_index];
281  parameters[0] = std::pow(semimajor, 6.5);
282  parameters[1] = test_e;
283  binary.differential_equations(age,
284  &(parameters[0]),
285  Core::BINARY,
286  &(evolution_rates[0]));
287  expected_semimajor_rate[e_index] = (
288  6.5 * std::pow(semimajor, 6.5)
289  *
290  rate_factor
291  *
293  orbital_frequency_multiplier,
294  spin_frequency_multiplier,
295  test_e
296  )
297  );
298  expected_eccentricity_rate[e_index] = (
299  test_e
300  *
301  rate_factor / 4.0
302  *
304  orbital_frequency_multiplier,
305  spin_frequency_multiplier
306  )
307  );
308  predicted_semimajor_rate[e_index] = evolution_rates[0];
309  predicted_eccentricity_rate[e_index] =
310  evolution_rates[1];
311  }
312 
313  std::ostringstream message;
314  message << "orbital freq. mult. = "
315  << orbital_frequency_multiplier
316  << ", spin freq. mult. = "
317  << spin_frequency_multiplier
318  << " for a = " << semimajor
319  << ", W* = " << primary_spin_frequency;
320  std::cout << message.str() << std::endl;
321 
322  output_rates(eccentricities,
323  expected_semimajor_rate,
324  predicted_semimajor_rate,
325  expected_eccentricity_rate,
326  predicted_eccentricity_rate);
327 /* check_agreement(
328  eccentricities,
329  expected_semimajor_rate,
330  predicted_semimajor_rate,
331  2,
332  4,
333  (
334  "Checking semimajor rate against Zahn (1977)"
335  +
336  message.str()
337  )
338  );
339  check_agreement(
340  eccentricities,
341  expected_eccentricity_rate,
342  predicted_eccentricity_rate,
343  2,
344  20,
345  (
346  "Checking eccentricity rate against Zahn (1977)"
347  +
348  message.str()
349  )
350  );*/
351  }
352  }
353  }
354  }
355  }
356 
358  {
359  const double MAX_ECCENTRICITY = 0.9;
360  const unsigned NUM_ECCENTRICITIES = 100;
361  const double ECCENTRICITY_STEP = (
362  MAX_ECCENTRICITY / (NUM_ECCENTRICITIES - 1)
363  );
364  const double a = 10.0;
365  const double age = 1.0;
366 
368  StellarEvolution::make_no_evolution();
369 
371  *no_evol,
372  1.0,
373  1.0,
374  1.0,
375  1.0
376  );
377 
378  Planet::Planet planet(1.0, 1.0);
379 
380  BinarySystem binary(*star, planet);
381 
382  std::valarray<double> parameters(0.0, 10),
383  rough_rates(parameters.size()),
384  fine_rates(parameters.size());
385 
386  parameters[0] = a;
387 
388  binary.configure(true,
389  (MIN_AGE + MAX_AGE) / 2.0,
390  &(parameters[0]),
391  Core::BINARY);
392  star->detect_saturation();
393 
394  std::cout << std::setw(25) << "e_order"
395  << std::setw(25) << "e"
396  << std::setw(25) << "rough_a_rate"
397  << std::setw(25) << "fine_a_rate"
398  << std::setw(25) << "rough_e_rate"
399  << std::setw(25) << "fine_e_rate"
400  << std::setw(25) << "rough_inc_rate"
401  << std::setw(25) << "fine_inc_rate"
402  << std::setw(25) << "rough_spin_rate"
403  << std::setw(25) << "fine_spin_rate"
404  << std::endl;
405 
406 
407  for(unsigned e_order = 5; e_order <= 100; e_order+=5) {
408  for(double e = 0.0; e <= MAX_ECCENTRICITY; e += ECCENTRICITY_STEP) {
409  parameters[1] = e;
410 
411  star->zone(0).change_expansion_order(e_order, binary, true, 0);
412  binary.differential_equations(age,
413  &(parameters[0]),
414  Core::BINARY,
415  &(rough_rates[0]));
416  star->zone(0).change_expansion_order(2 * e_order,
417  binary,
418  true,
419  0);
420  binary.differential_equations(age,
421  &(parameters[0]),
422  Core::BINARY,
423  &(fine_rates[0]));
424 
425  std::cout << std::setw(25) << e_order
426  << std::setw(25) << e
427  << std::setw(25) << rough_rates[0]
428  << std::setw(25) << fine_rates[0]
429  << std::setw(25) << rough_rates[1]
430  << std::setw(25) << fine_rates[1]
431  << std::setw(25) << rough_rates[2]
432  << std::setw(25) << fine_rates[2]
433  << std::setw(25) << rough_rates[7]
434  << std::setw(25) << fine_rates[7]
435  << std::endl;
436  }
437  }
438 
439  delete star;
440  delete no_evol;
441  }
442 
443 } //End Envolve namespace.
Implements a StellarEvolution based on polynomial evolution quantities.
const double MIN_AGE
Most tests start at this age in Gyr.
Definition: Common.h:54
void detect_saturation()
Sets the saturation based on the currently configured spin frequency.
A skumanich wind body with a single zone dissipative to only a single tidal term. ...
double zahn77_semimajor_rate_coef(int orbital_frequency_multiplier, int spin_frequency_multiplier, double eccentricity) const
The Zahn (1977) coefficient for the semimajor evolution rate that corresponds to the given tidal term...
void test_aligned_equations()
Test the a & e differential equations for aligned orbit.
Single zone non-evolving planets with huge dissipation, so they always remain locked to the disk...
Definition: Planet.h:21
Evolve::DissipatingZone & zone(unsigned zone_index)
See Evolve::DissipatingBody::zone().
Definition: EvolvingStar.h:85
const double MAX_AGE
Most tests end at this age in Gyr.
Definition: Common.h:57
Orientations of zones of bodies in a binary system.
int differential_equations(double age, const double *parameters, Core::EvolModeType evolution_mode, double *differential_equations)
The differential equation and jacobian for the evolution of the system.
double mass() const
The mass of the body (constant with age).
const double solar_radius
Radius of the sun [m].
void check_agreement(const alglib::real_1d_array &x, const alglib::real_1d_array &y1, const alglib::real_1d_array &y2, unsigned agreement_order, unsigned max_order, const std::string &description)
Check if two given dependencies on x agree up to a given order in x.
const double Gyr
Gyr [s].
const double solar_mass
Mass of the sun [kg].
virtual void change_expansion_order(unsigned new_expansion_order, BinarySystem &system, bool primary, unsigned zone_index)
Changes the order of the eccentricity expansion performed.
double zahn77_eccentricity_rate_coef(int orbital_frequency_multiplier, int spin_frequency_multiplier) const
Same as zahn77_semimajor_rate_coef() but for the eccentricity evolution.
Unit tests that check the differential equations for eccentricity and semimajor against analytic expr...
void test_error_estimate()
Test the error estimate of the differential equations.
virtual void change_expansion_order(unsigned new_expansion_order)
Change the tidal potential expansion order for all dissipative zones.
Star::InterpolatedEvolutionStar * make_const_lag_star(const StellarEvolution::Interpolator &evolution, double wind_strength, double wind_sat_freq, double coupling_timescale, double phase_lag)
Create a star with the given parameters with a constant phase lag.
Definition: MakeStar.cpp:10
virtual int configure(bool initialize, double age, double semimajor, double eccentricity, const double *spin_angmom, const double *inclination, const double *periapsis, Core::EvolModeType evolution_mode)
Sets the current state of the system.
Describes a system of two bodies orbiting each other.
Definition: BinarySystem.h:57
void output_rates(const alglib::real_1d_array &eccentricities, const alglib::real_1d_array &expected_semimajor_rate, const alglib::real_1d_array &predicted_semimajor_rate, const alglib::real_1d_array &expected_eccentricity_rate, const alglib::real_1d_array &predicted_eccentricity_rate) const
Output the predicted/expected semiamjor and eccentricity evolution rates to stdout.
const double G
Gravitational constant in SI.