Generating Terrestrial Planets Pt.1

This is related to Semi-Empirical Stellar Equations. I have had some questions easily answered, and others which are difficult or impossible to get the kind of accuracy I want.

I’m going to order what I’ve learned by the order I am planning to use these equations, marking equations loosely based on empirical data with an asterisk, and double asterisks for things I’ve completely made up to simplify calculation.

  • Radius of Surface* (Rsurface):
    5500km mean, 1550km standard deviation
  • Density* [based on common rock types] (Dplanet):
    4.7g/cm3 mean, 0.55g/cm3 standard deviation
  • Volume [assumes a perfect sphere] (Vplanet):
    4/3 * π * Rsurface3
  • Mass (Mplanet):
    Dplanet * Vplanet
  • Surface Gravity (gsurface):
    6.673×10-11Nm2kg-2 * Mplanet / Rsurface2
  • Surface Area [assumes a perfect sphere]:
    4 * π * Rsurface2
  • Atmospheric Pressure Reduction Rate** (Pr):
    Pr [unit: none] = -0.00011701572 [unit: s2 / m2] * gsurface
  • Atmospheric Halving Height** (hd):
    hd [unit: m-1] = ln(2) / Pr
  • Atmospheric Volume at Constant Pressure** (Vsim):
    (4/3 * π * (Rsurface + hd)3 – Vplanet) * 2
  • Surface Atmospheric Pressure (Psurface):
    Psurface [unit: kPa] = ???
  • Lowest Safe Orbital Height** (h0):
    h0 [unit: m] = ln(1.4×10-11 / Psurface) / Pr
  • True Atmospheric Volume (Vatm):
    4/3 * π * (Rsurface + h0)3 – Vplanet

Vsim exists to support a simulation of an entire planet’s atmospheric contents using my simple fluid simulation mechanic. The magic constant used to calculated Pr is based on Earth’s atmosphere, it can be changed to set an exact “atmospheric height” while maintaining other properties of this simulation.

  • Atmospheric Pressure at Altitude** (Paltitude):
    Paltitude [unit: kPa] = Psurface * exp(Pr * maltitude) [Pr may be -Pr, I don’t remember]

This post is obviously incomplete, but it’s been a while since I posted anything here, and I felt like I should go ahead and share what I’ve been up to.

Semi-Empirical Stellar Equations

I’ve spent a lot of time trying to answer certain questions in astronomy, where I just want a rough approximation for the purpose of a simulation, and don’t need an exact answer. These are some of the equations I’ve come up with.

Yerkes Classes’ T/Mv Relations

These equations are shown as functions where x is temperature in Kelvin, and f(x) is absolute magnitude (visual). Most are valid from 2400 K to a little bit past 30000 K, the exceptions are noted. For the hypergiants and white dwarfs, the range is within an elliptical region, of which these functions define the major axis; for all others, they are a trend line along a Yerkes classification.

  • Hypergiants (0):
    f(x) = -8.9
  • Supergiants (Ia):
    f(x) = -0.00135x3 + 0.0233x2 + 0.0187x – 7.349
  • Supergiants (Ib):
    f(x) = 0.00329x3 – 0.0962x2 + 0.829x – 7.209
  • Bright Giants (II):
    f(x) = 0.00557x3 – 0.166x2 + 1.505x – 6.816
  • Giants (III):
    f(x) = 0.0135x3 – 0.373x2 + 3.019x – 7.233
  • Subgiants (IV):
    f(x) = -0.151x2 + 2.216x – 5.128 (4450-100000 K only)
  • Dwarfs (V):
    f(x) = 0.00193x5 – 0.0615x4 + 0.742x3 – 4.257x2 + 12.439x – 12.996 (note: this one is the least accurate)
  • Subdwarfs (VI):
    f(x) = 0.131x3 – 3.275x2 + 27.576x – 71.7 (3050-6000 K only)
  • White Dwarfs (VII):
    f(x) = 0.489x + 10.01 (4450-30000 K only)

Simple Conversions

  • Absolute Magnitude (visual) → Luminosity (solar luminosities):
    Lsun = 100 * exp(-0.944 * Mv)
    Accurate between 0-10 Mv. Probably continues accuracy relatively well.
  • Main Sequence Luminosity / Mass Relation:
    Lstar / Lsun = (Mstar / Msun)3.5
    Lstar = ( 3.68 * ln(Mstar) – 0.244 )e
    (The second equation applies to all stars.)
  • Mass / Lifetime Relation:
    Tyears = ( 23.4 – 2.68 * ln(Mstar) )e
    (This applies to all stars.)

Spectral filters are used to help classify stars. Ultraviolet, Blue, Visual, Red, and Infrared. Objects are listed in different indexes:

  • UV for the hottest objects (stellar remnants, galaxies)
  • BV (the majority of stars)
  • RI for the coolest (LTY “stars” and below)

Equations I no longer recommend

Provided for completeness, in case they are found to be useful.

  • Temperature (K) → Absolute Magnitude (visual):
    Mv = 35.463 * exp(-0.000353 * T)
    Roughly accurate between 2000-50000 Kelvin. Probably doesn’t continue accuracy at hotter temperatures. Previously this was at the top of this post, now I am not sure why (perhaps the simplicity). The Yerkes’ classifications are a better system.
  • B-V index (x) → Temperature (K):
    T = -772.2x3 + 3152x2 – 6893x + 9500
    Sorta accurate between 0-2. (This equation I am least comfortable with, and don’t plan to use.)

Better Fluid Storage

A while back, I posted a prototype fluid storage system with a mechanic for handling breaches in a pressurized system. I thought I’d be clever by storing fluids as a percentage of a defined volume and pressure. For a “simplified” system, it was quite complicated, and fundamentally flawed.

This time, it is straightforward. Keep track of the amounts of each fluid, a total sum, and volume of the container. Pressure is the sum divided by the volume, and the percent of a fluid is its amount divided by the total sum of all fluids.

tank = {
  volume: 200, sum: 300,
  contents: { hydrogen: 200, oxygen: 100 }
}
pressure = tank.sum / tank.volume -- 1.5
percent_hydrogen = tank.contents.hydrogen / tank.sum -- 0.67

Everything needed from a container can be accessed immediately or with a single calculation involving only two variables.

But what about hull breaches?

Fluids vs Mechanical Classes

I realized that I should define fluid containers very narrowly, all they need care about is a small set of numbers, and have a few functions to modify that state. Enter the Breach class.

Breach(fluidA, fluidB, volume)

Specify which fluid containers are interacting and the volume ( I guess technically it should be area) of the breach. Each update cycle moves the pressure difference multiplied by the volume (area) of the breach from the higher pressure container to the lower pressure container.

What about pumps? I have those, with a “volume” and a “rate” modifier to allow you to adjust how fast the pump works. Pumps only work in one direction, but have a function to reverse them.

Want only one fluid to go through..say, a filter? Made that as well. Valves, so that you can adjust flow rate, filter-pump combos for pushing just the right amount of one fluid, and one-way valves to allow pressure to escape but not allow any blowback.

The Flaws

  • Once pressure is equalized, contents do not mix between fluids.
  • All fluids have the same density. This probably isn’t that hard to fix, but is unneeded for my purposes.
  • All fluids mix. This may or may not be harder to fix depending on how it is approached.
  • Temperature isn’t simulated at all. I would love to have heat transfer and heat affecting density, but these details are not necessary for my current project.

The Code

As of publishing this article, I don’t have a library to give you, but I will update it as soon as I do release it. For now, here is where I have the beginnings of a library. (Updated 2024-10-11 to fix the broken link.)

Grammar-Based Generation

To me, this is a new form of procedural generation. You declare specific rules for your desired content, and then a generator runs accordingly. I’ve only seen it used for text, but I’m sure the same technique works for anything. The simplest example is picking a random item from a list, and a slightly more complex version shows the power of defining a grammar:

grammar = {
  "first name": "Anna", "Belle", "John"
  "last name": "Brown", "Jameson", "Williams"
  "full name": "{first name} {last name}"
}

G(grammar, "full name") -- ex: Anna Williams

The syntax above is pseudocode for a generator I am working on. I plan to allow the use of a custom seed along with the generator so that you can do things like have uniquely generated people for a population, where only a lookup number needs to be stored (if you wish to remember a specific person).

It gets even more powerful when you make it possible to define multiple versions of the same grammar and use different ones depending on an object’s properties, and allow inline code within the grammars. Here’s an incomplete example based on my continuing efforts to build space games:

{
  "system name": {
    {
      props: { pulsar: true }
      "PSR {random(1000)}"
    }
    {
      props: { pulsar: false }
      "{Greek letter} {Latin name}"
      "{random(100)} {Latin genitive}"
      "{modern constellation} G{random(1000,9999)}.{random(1000,9999)}"
    }
  }
}

For this example, pulsars would get their traditional “PSR ###” names, while non-pulsars would get names based on differing classification methods.

I’m currently thinking about a game based on Aurora, but massively simplified and playable in-browser. Grammar-based content generation would play a very important role in this, from generating system names (as above) to NPC ship design.

References

(Note: All resources are archived using the services linked to on Archives & Sources.) Resources that helped me recognize the potential of grammars:


(Normally I would like to publish working code along with these posts, or some other form of useful data, but today we’re looking at a work-in-progress idea without even that much concrete form.)

Simplified Fluid Storage System

(A flaw in the design of this system was fixed and posted about here.)

One of my game ideas involves constructing 2D spaceships, and the concept of a simplified system for storing fuel, oxygen, water – really any kind of fluid mixture – in storage tanks. Along with this, it allows simulating breaches between containers, hard vacuum, and the pressurized areas of the ship itself!

{ -- a rough approximation of Earth's atmosphere
  pressure: 1
  volume: 4.2e12 -- 4.2 billion km^3
  contents: {
    nitrogen: 0.775
    oxygen: 0.21
    argon: 0.01
    co2: 0.005
  }
}

{ -- hard vacuum
  pressure: 0
  volume: math.huge -- infinity
  -- the contents table will end up containing negative infinity of anything that leaks into the vacuum
}

It all comes down to storing a total pressure and volume per container, and a table of contents as percentages of the total mixture. The total amount of mass in the system can be easily calculated (volume * pressure), as can the amount of any item in the system ( volume * pressure * percent).

Breaches are stored as a reference in the container with a higher pressure, and a size value is added to the container with lower pressure (representing the size of the hole between them).

screenshot of testing my fluid system
A sequence of tests.

Limitations

  • Everything has the same density and mixes evenly.
  • There are no states of matter, everything is treated as a gas.
  • Attempting to directly modify the amount of a fluid is prone to floating-point errors it seems, while mixing containers via the breach mechanic is working as expected.

The code was written in MoonScript / Lua and is available here.


Updated 2024-10-11 to fix broken link.