No description
Find a file
2025-12-09 21:18:44 +08:00
.gitignore At this stage I'm not sure if this should be a Godot project 2025-12-09 21:18:44 +08:00
README.org More star stuff, planet stuff 2025-12-08 22:04:17 +08:00

Open Heavens

Open Heavens is a semi-realistic and deterministic star system generator, based on Stardate Legacy. Open Heavens provides the "controller" part of your average MVC, so it does not provide any visualisation or interaction logic with the system (except for whatever is needed for testing).

Dependencies & Credits

Generation Process

Open Heavens generates systems by their mass, age (which is sparingly used), and elemental abundance, both of which are configurable when passed to the system generator. This process outline assumes you leave all settings as random, and only provide a seed. Most rates are configurable.

We start by randomly generating the solar mass of the overall system. 80% of the time, we use a random distribution of a 1.1 solar mass mean with a 0.5 solar mass deviation, and we clamp this to 0.5 and 20 on the lower and upper end. 20% of the time, we just randomly generate anywhere from 0.5 to 20 solar masses. We also generate the age of the system, in gigayears - 80% of the time this is a normal distribution with a mean of 5 and deviation of 1, otherwise it's a random value between 1 and 13. The reason for these values is that they provide 80% (this is configurable) odds of generating an interesting system with a good number of exoplanets.

We next generate abundances. The top ten most common elements are normally distributed around their mass fraction with small (0.05 - 0.00005) deviations. For the remaining natural elements, they are completely random but always add up to 100%. This is not a particularly realistic way of generating the abundance, but it leads to some fun results like thorium worlds and the like.

We generate the star of the system by removing a random portion of the total system mass - between 20% and 99%. This mass makes up the star and is used to generate it.

Star Generation

Aside from mass and age, we calculate or generate the following star values:

  • Stellar Class
  • Temperature
  • Color (RGB)
  • Luminosity (Visible Light, X-Ray, and Extreme UV)
  • Radius
  • Density
  • Habitable Zone Range
  • Composition (and Metallicity)
  • Stellar Wind Quantity (yearly mass loss)
  • Stellar Wind Velocity
  • Magnetic Field Strength
  • Average Solar Flare Energy & Average Flares per Day
  • Stellar Variability

Stellar Class

We start by calculating the stellar class - since we only have main sequence stars this is fairly simple as we just look at the mass of the star and assign the class based on the range:

Mass (Solar Masses) Class
x < 0.45 M
0.45 < x < 0.8 K
0.8 < x < 1.04 G
1.04 < x < 1.4 F
1.4 < x < 2.1 A
2.1 < x < 16 B
x > 16 O

Temperature & Color

We use a curve to determine the temperature (in Kelvin) from the mass, and color (RGB) from the temperature. These curves used known stars as sample points.

Luminosity

For luminosity, the following DISGUSTING formula is used. 14341850000 + ((78.43685-14341850000)/(1 + pow(temperature/145852.2,7.237745)))-78.46 This gives the luminosity in solar luminosity, so 1 = 3.75×10^28 lumen.

Radius

To calculate the radius (in solar radii), we first calculate a best guess: pow((3.0 * mass)/4.0*PI, 1.0/3.0). The output of this is 0.62035 for 1 solar mass, and so we normalise the value so that 1 solar mass = 1 solar radius: (best guess/0.62035) - 1.14503109452965

Density

From the radius we calculate the density, which is used for habitable zone and Roche limit calculations. We do this by calculating the projected volume - (4.0/3.0) * PI * pow(solar_radii * SOLAR_RADIUS, 3) - and real mass (in Kg) - mass * 1.9885 - which gives us a simple formula for the kg/m^3 density: (r_mass/projected_volume) * 1000 * 1000.

Habitable Zone

We calculate the inner and outer radius of the star's habitable zone as the square root of the luminosity divided by 1.1, and the square root of the luminosity divided by 0.53 respectively. These values are in AU.

Composition

We also determine the composition of the star, however we assume it is only composed of hydrogen and helium. Hydrogen can be any percent between 65 and 85, while helium is the remainder to bring the composition to 100. This prevents too much hydrogen and helium from being used in planet generation, which prevents too many gas giants from being generated. However, though we don't calculate the fine-grained composition, we do calculate a metallicity based on age (in Solar Metallicities): for systems with an age between 1 and 8, this is a random value between 0.5 and 2.0. For older systems, it's between 0.001 and 0.1.

Stellar Wind

We also need to estimate the quantity of stellar wind, which helps us determine whether planets in proximity to the star should have atmospheres - as well as undestanding the general system's radiation. We calculate the mass loss rate differently depending on the class of star. For this we calculate the rotation period as 25 * pow(mass, -1.7) * pow(age/4.6, 0.5) and convective turnover time as 70 * pow(mass, -2.5). We can also then calculate the Rossby number as the period divided by the turnover time, divided by 2. For stars less than 1.5 solar masses in size, mass loss is driven by magnetic activity and is calculated as pow(radius in km, 2) * pow(convective turnover time, -2.6) * pow(rotation period, -2.6), divided by the 1 Solar mass to get the mass lost per year. For stars greater than 1.5 solar masses, the formula is instead: k * pow(luminosity, v) * pow(mass, q) * pow(metallicity, 0.69) divided 1 Solar mass. The values of k, v, and q change based on mass:

Mass k v q
1.5 < x 3 pow(10, -13.8) 1.24 -0.16
3 < x < 10 pow(10, -13.6) 1.69 -0.84
10 < x pow(10, -12.95) 1.58 -0.67

We also need to calculate the terminal wind velocity of the stellar wind. This is 617.7 * sqrt(mass / radius) km/s.

Magnetic Field Strength

We can calculate the magnetic field strength based on mass as well, with stars below 0.35 solar masses having a random strength between 1000 and 5000 Gauss. Stars between 0.35 and 1.5 solar masses calculate their strength from their rotation period at convective turnover: pow((period / convective turnover)/2, -1.2) kilo-Gauss. For stars greater than 1.5 solar masses, the value is random between 100 and 1000 Gauss.

X-ray and Extreme UV Luminosity

The X-ray luminosity is calculated as pow(10, -3.7) * pow(Rossby, -2), divided by the luminosity. The EUV luminosity is just 4 times the X-ray luminosity.

Solar Flare Frequency & Strength

For stars above 1.5 Solar masses, the flare frequency is a random on a normal distribution with a mean of 0.1 and a deviation of 0.005. For smaller stars, the flare frequency is 10 * pow(age, -1.5).

We can calculate the average solar flare energy as x-ray luminosity * pow(Rossby, -2).

Stellar Variability

Variability is important for modelling climate stability, and is calculated as 0.001 * pow(Rossby, -1.5). The starspot cycle period is calculated as 10 * pow(rotation period, 0.8) years.

Planet Generation

We always generate a minimum of 1 planetoid, which can be forced to be habitable. We continue generating planets up until all the system mass is used up - that being when only 0.05 Earth masses are remaining, which is considered as the mass for non-planet system features such as asteroids or comets.

Each time we generate a planet, we generate a mass for it (in Earth masses) and subtract that from the remaining system mass. If the planet should be forced as habitable, the mass is always between 0.107 and 9.44 Earth masses, otherwise it is either between 0.05 and 1000, or 0.05 and 15, where we select the former a third of the time. This prevents too many gas giants from showing up.

To determine the radius of the planet (in Earth radii), for most planets we sample the planet mass to radius curve (which is constructed based on real known planet mass and radius values), adding some normally distributed deviation (mean of 0, deviation of 0.15). For planets with forced habitable generation, we start with a random radius between 0.5 and 2.49. We then incrementally subtract and add from this radius until we can get an estimated surface gravity between 0.4 and 1.7. We calculate the density after the radius, similarly to the star density calculation:

  1. We get a projected volume: (4.0/3.0) * PI * pow(planet_radius * 6378 * 1000, 3)
  2. We get the real mass in kilograms: mass * 5.98 * pow(10, 24)
  3. We divide the latter by the former to get the density

Surface gravity is calculated from the kilogram mass (mass * 5.98 * pow(10, 24)) and meter radius (planet_radius * 6378 * 1000). This gives a gravity of G * (real_mass / pow(real_rad, 2)) meters/second^2, and m/s^2 * 0.10197162129779 g's.

We calculate each planet's orbit. If the planet is forced habitable, we set the orbit distance to a random value within the star's habitable zone range. Otherwise, we calculate the roche limit based on the radius, mass, and density. If the parent body of this planet is a star (the system root) then we randomise the orbit between 0.05 AU + the roche limit and 50 AU + the outer habitable zone radius. If the parent body is a planet, we randomise it between the roche limit and 0.1 AU.

We calculate the orbit speed as sqrt((G * parent_mass)/distance_at_apoapsis_m) / 1000, using real values, where the distance at apoapsis is (orbit_distance * 1.495979)*100000000. The orbital period follows as (2 * PI * orbit_distance)/orbit_speed, and we turn that into an "Earth days" measurement with abs(orbital_period / 0.211 * 365).

We also calculate a rotation period - for forced habitable worlds, this value is a normal distribution with a mean of 1 day and a deviation of 0.2. For every other world, this is a normal distribution with a mean of 1 divided by the mass of the planet, and a deviation of 1. The rotation period has a 50% chance of being reversed. For forced habitable worlds, the axial tilt is random between 0 and 35 degrees, otherwise it's random between 0 and 90 degrees. If the rotation period and orbital period are approximately equal, we also keep track of the planet being tidally locked.

Each planetoid can also have moons, each of which being their own planetoid which is flagged as being unable to generate moons. The chance of generating a moon is based on the parent planetoid mass and is random - a moon is generated 20% of the time if the parent is less than 10 Earth masses, and 50% of the time otherwise. This chance is divided by the number of moons + 1, to progressively generate less and less moons.

Non-moon planetoids can also have rings. The chance of a ring generating is also based on the planetoid mass. We check if clampf(randf(), 0.0, 0.8)/mass is less than 0.0065, meaning that at a planet of Jupiter's mass we have a 65% chance of generating a ring.

Rings, or more accurately ring systems, can have multiple segments. This is a normal random distribution with a mean of 4 and a deviation of 3, with a minimum of 1. There are three types of rings - Icy, Dusty, and Rocky. Dusty has a 50% chance chosen as the ring type, while Icy and Rocky both have a 25% chance.

We assign a mass to the ring, which is a random number between 0.000001 and 0.05 Earth masses. We also determine the rings inner and outer radius - the inner radius is a random number between the Roche limit for the chosen mass and 0.05 AU, while the outer radius is a random number between the inner radius and 0.1 AU.

We calculate the ring's composition by using the starting system abundances. As with everything else, rings can only be composed of natural elements. We select up to 10 elements from the system abundance (sorted by most to least abundant) to make up the composition of the ring. The first element chosen accounts for anywhere between 0 and 100% of the composition, and each subsequent element makes up a random percent between 0 and 100 minus the current sum total of elements in the system.

Planets are generated based on a classification system, with possible planet classes being Terrestrial, Metallic, Water, Gas, and Ice. By default, every planet can be metallic or terrestrial, and not ice, gas, or water. This is modified by mass and a few other factors:

  • If a planet is greater than 10 Earth masses and is not a moon, it could be a gas planet.
  • If the mass is less than 15, it could be an ice or water planet.
  • If the mass is greater than 5, it can't be either a metallic or terrestrial planet.
  • If the planet is not within the star's habitable zone, it cannot be a water planet.
  • If the planet is closer than the other radius of the star's habitable zone, it cannot be an ice planet.

These rules provide some base sanity for planet generation and prevent things like small gas giants. We then roll a random float.

  • If it could be ice and the number is between 0.7 and 0.9, the planet is an ice planet.
  • If it could be water and the number is greater than 0.9, it is a water planet.
  • If it could be metallic and the number is between 0.5 and 0.7, it's metallic.
  • If it could be terrestrial, it's terrestrial.
  • If it could be gas, it's gas.
  • And finally as a fallback, if none of the previous conditions match, the planet is terrestrial.

The atmosphere is also based on a classification system, with atmospheres being either None, Light, Earth-like, or Heavy.

Limitations

  • Elemental abundances are fully random beyond the top 10.
  • Non-unary system generation is not implemented because I don't want to write the orbit math for it.
  • Only main sequence stars are currently generated.
  • Planetary orbits are circular, because I'm too lazy to do it otherwise.

FAQ

What does "semi-realistic" mean?

I am not an astrophysicist, geologist, or any other qualified position. I am just a computer programmer who wants to recreate Elite: Dangerous' galaxy generation logic. "Semi-realistic", in the context of this project, means "close enough" to realistic for simulation purposes or for the meta-setting this is inspired by (see the Stardate Legacy repo for more details). I am open to pull requests to correct any incorrect calculations or information, particularly from people with credentials in the field of whatever you are trying to correct.