-
Notifications
You must be signed in to change notification settings - Fork 0
Criei uma estrela usando Java
Eu estava estudando um jogo chamado Capitalism Lab e, em curtas palavras: “Ele é um simulador de empresas, economia e capital”. Meu objetivo é criar um clone dele para Web, visto que ele não tem uma release para Linux. Passei um tempo com isso e fazendo coisas para ter uma economia “factível” como é o jogo, mas aí eu percebi que a economia é moldada através do meio. Poxa, sei que isso é meio obvio (agora), mas quando comecei eu só queria saber de ter uma conta em um banco e começar minha rede de varejo! Só que para chegar nesse ponto, fiquei em um loop de ficar fazendo modelagens para ter uma conta bancaria, mas para o banco funcionar precisa existir dinheiro. Para o dinheiro existir precisei definir de onde ele vinha e aí foram surgindo outras entidades e regras…
Quando tomei a ideia de fazer o mais realista que eu conseguisse, mais até do que o jogo que eu mencionei, tudo tinha que fazer um sentido lógico, mesmo que não fosse apresentado explicitamente para quem estiver jogando. O Capitalism Lab não tem estações, mas estações influenciam a economia. Ele não tem eventos climáticos adversos, mas esses eventos influenciam a economia. Meu objetivo então passou a ter o simulador mais factível que eu pudesse ter, não excluindo elementos auxiliares como eventos scriptados.
Para chegar nesse resultado, parei o que estava fazendo e fui estudar qual seria a camada mais alta para uma simulação econômica. A conclusão que eu cheguei foi o Sol.
Mas veja bem, o que eu sei sobre astronomia é bastante superficial, mas graças a internet e a IA, fui capaz de chegar em um resultado que me agradou muito. Esse artigo é a consolidação dos estudos e pensamentos que tive para gerar uma estrela que fosse parecida e tendo como base: o Sol.
“Para fazer uma torta de maçã do zero, primeiro você precisa inventar o universo.” Carl Sagan
Só para uma recapitulação importante: "Uma estrela é uma enorme esfera de plasma mantida unida pela gravidade, dentro da qual acontecem processos de fusão nuclear que liberam uma quantidade gigantesca de energia." *Stars: Crash Course Astronomy #26.* Outros materias, caso se interesse, vão estar lincados nas fontes no final do artigo.
public class Star {
private UUID id;
private String name;
private SpectralType spectralType;
private Double massSolar;
private Double radiusSolar;
private Integer temperatureKelvin;
private Double luminositySolar;
// getters e setters
}Repara que massa, raio e luminosidade estão em unidades relativas ao Sol. Isso é convenção muito comum em astrofísica pois evita ficar carregando a massa solar de 1,989 × 10³⁰ kg, podemos simplesmente dizer que ela possui 2 M☉. Também repare que, apesar disso ser uma constante no código, M☉, R☉ e L☉ são, na realidade, valores de referência baseados no Sol. Eles não são constantes fundamentais da natureza, como a constante gravitacional (G) ou a velocidade da luz (c).
Estrelas são classificadas principalmente de acordo com a temperatura de sua superfície usando o sistema O, B, A, F, G, K, M. Para representar isso no nosso modelo, temos o Enum SpectralType.
Essa classificação combinada com a luminosidade dá origem ao Diagrama de Hertzsprung–Russell: um gráfico de luminosidade × temperatura onde cerca de 90% das estrelas (o Sol incluído) caem numa faixa diagonal chamada "sequência principal". É de onde vêm as faixas de massa, raio e temperatura que uso no enum. Estudei isso principalmente em três lugares: o laboratório interativo The Hertzsprung-Russell Diagram (NAAP), o material O Diagrama HR, do Instituto de Física da UFRGS, e a Wikipédia em português sobre o Diagrama de Hertzsprung-Russell. Saber a luminosidade importa porque ela influencia diretamente a temperatura de um planeta em órbita, mas isso é algo que vou deixar para um próximo artigo…
No código, cada classe espectral carrega a faixa de massa, raio e temperatura que uma estrela real daquele tipo costuma ter:
public enum SpectralType {
O(16.0, 90.0, 6.6, 15.0, 30_000, 52_000),
B(2.1, 16.0, 1.8, 6.6, 10_000, 30_000),
A(1.4, 2.1, 1.4, 1.8, 7_500, 10_000),
F(1.04, 1.4, 1.15, 1.4, 6_000, 7_500),
G(0.8, 1.04, 0.96, 1.15, 5_300, 6_000),
K(0.45, 0.8, 0.7, 0.96, 3_900, 5_300),
M(0.08, 0.45, 0.1, 0.7, 2_300, 3_900);
private final double minMassSolar, maxMassSolar;
private final double minRadiusSolar, maxRadiusSolar;
private final int minTemperatureKelvin, maxTemperatureKelvin;
// construtor + getters
}Essas faixas são o que impede o gerador de inventar uma estrela azul de 30.000 K com massa de anã vermelha, pense como um salvaguarda fisicamente correto.
Se eu simplesmente sorteasse a classe espectral com probabilidade uniforme eu teria um universo irreal. Cerca de 76% das estrelas são anãs M (pequenas, frias, vermelhas), e estrelas O são quase inexistentes (~0,00003%). É o que mostram o vídeo Anãs vermelhas e vida no Universo, da série O Universo: Mistérios Revelados (HISTORY) e a matéria Quais são os tipos de estrelas anãs: o que você precisa saber, da National Geographic Brasil. Anãs vermelhas dominam porque nuvens gigantes de gás no espaço se fragmentam muito mais facilmente em pedaços pequenos do que em massas gigantescas durante o colapso gravitacional que dá origem às estrelas (Formação Estelar, também do Instituto de Física da UFRGS).
private static final double[] SPECTRAL_TYPE_WEIGHTS = {
0.0000003, // O
0.0013, // B
0.006, // A
0.03, // F
0.076, // G
0.121, // K
0.7657 // M — soma dos pesos = 1.0
};Esse mesmo problema poderia aparecer se eu gerasse massa, raio e temperatura de forma independente, teria a situação de estrela impossível, que é algo que eu não entendi muito bem mas vou estudar mais sobre.
Só um ponto que eu preciso deixar claro antes de você continuar vendo o código: tem muita simplificação no código. Afinal de contas, eu não sei tudo que precisa para gerar uma estrela. Não saberia calcular átomo por átomo para chegar no resultado correto.
public Star generate(String name, RandomGenerator random) {
SpectralType spectralType = pickSpectralType(random);
double positionInClass = random.nextDouble();
double massSolar = RandomRanges.interpolate(
spectralType.getMinMassSolar(), spectralType.getMaxMassSolar(), positionInClass);
double radiusSolar = RandomRanges.interpolate(
spectralType.getMinRadiusSolar(), spectralType.getMaxRadiusSolar(), positionInClass);
int temperatureKelvin =
(int) Math.round(RandomRanges.interpolate(
spectralType.getMinTemperatureKelvin(), spectralType.getMaxTemperatureKelvin(), positionInClass));
Star star = new Star();
star.setId(RandomIds.nextUuid(random));
star.setName(name);
star.setSpectralType(spectralType);
star.setMassSolar(massSolar);
star.setRadiusSolar(radiusSolar);
star.setTemperatureKelvin(temperatureKelvin);
star.setLuminositySolar(starPropertiesCalculator.calculateLuminositySolar(star));
return star;
}E essa é uma das regras de negócio mais importantes dessa primeira parte da simulação e isso nunca pode ser um valor aleatório, precisa ser uma consequência das propriedades físicas da própria estrela, principalmente de seu raio e de sua temperatura efetiva via Lei de Stefan-Boltzmann. Em termos simples, todo corpo com temperatura acima do zero absoluto emite radiação térmica, e a quantidade de energia emitida por unidade de área cresce com a quarta potência da temperatura. Isso significa que, mantendo todo o resto constante, dobrar a temperatura não dobra a energia emitida: ela aumenta por um fator de 2⁴ = 16.
Estudei essa relação na Wikipédia em português sobre a Lei de Stefan-Boltzmann e no material Extremos de luminosidade, raios e densidades, do Instituto de Física da UFRGS. E agradeço ao meu colega e amigo Victor Hugo pela solicitação para destrinchar a fórmula para que todos possamos entender seu funcionamento.
A Lei de Stefan–Boltzmann, originalmente, descreve a potência térmica emitida por unidade de área de uma superfície. Para uma estrela, podemos aproximar sua superfície como a de um corpo esférico. Assim, precisamos considerar não apenas a energia emitida por cada unidade de área, mas toda a superfície da estrela. A área de uma esfera é:
A = 4πR²
A potência emitida por unidade de área é dada por:
F = σT⁴
Multiplicando uma coisa pela outra, chegamos à luminosidade total:
L = 4πR² · σT⁴
É essa equação que interessa para a simulação. Mas vale a pena parar aqui e entender o que cada termo significa, porque cada parte dessa expressão possui uma consequência física diferente:
- L = luminosidade da estrela ou seja, a quantidade total de energia emitida por segundo em watts (W)
- R = raio da estrela
- T = temperatura efetiva da superfície da estrela medida em kelvin (K)
- σ = constante de Stefan–Boltzmann
- 4πR² = área da superfície da estrela, considerando-a como uma esfera
A fórmula, portanto, está combinando duas coisas diferentes: quanto de energia cada pedaço da superfície consegue emitir e quanto de superfície existe para emitir essa energia. É justamente por isso que o raio aparece elevado ao quadrado e a temperatura elevada à quarta potência. O termo R² não surgiu arbitrariamente. Ele vem diretamente da geometria de uma esfera. Se aumentarmos o raio de uma estrela, não estamos apenas aumentando uma dimensão linear. A superfície inteira cresce. Uma estrela com o dobro do raio terá uma área superficial de:
4π(2R)² = 4π · 4R² = 4(4πR²)
Ou seja, quatro vezes a área. Consequentemente, se duas estrelas possuem a mesma temperatura, mas uma possui o dobro do raio da outra, a maior terá quatro vezes a luminosidade. Podemos representar essa dependência simplesmente como:
L ∝ R²
O símbolo ∝ significa “é proporcional a”. Nesse caso, significa que, mantendo a temperatura constante, a luminosidade cresce com o quadrado do raio.
Outra coisa importante é que a Lei de Stefan–Boltzmann estabelece que a potência emitida por unidade de área é proporcional a T⁴:
F = σT⁴
Isso produz uma dependência extremamente forte da luminosidade em relação à temperatura. Se mantivermos o raio constante e aumentarmos a temperatura em duas vezes:
L ∝ (2T)⁴
Então:
L ∝ 2⁴T⁴
L ∝ 16T⁴
Portanto, a estrela passa a emitir 16 vezes mais energia por unidade de área. Perceba que esse efeito é independente do aumento de área causado pelo raio. O raio controla apenas o quanto de superfície existe, enquanto a temperatura controla quanto de energia cada unidade dessa superfície consegue emitir. Então, podemos resumir a dependência da luminosidade como:
L ∝ R²T⁴
Isso já nos dá uma intuição importante para a simulação: pequenas mudanças na temperatura podem produzir mudanças muito grandes na luminosidade, enquanto o raio possui uma influência quadrática.
Mas… na simulação, o objetivo dessa primeira etapa não é obter a luminosidade absoluta de cada estrela em watts (pode ser útil para o futuro, mas não agora). O que interessa é saber como a luminosidade de uma estrela se compara à luminosidade do Sol. Em vez de trabalhar diretamente com:
L = 4πR²σT⁴
Podemos utilizar o próprio Sol como referência aplicando a mesma equação:
L / L☉ = (4πR²σT⁴) / (4πR☉²σT☉⁴)
E é aqui que a fórmula começa a ficar interessante. Como 4π aparece tanto no numerador quanto no denominador, ele se cancela. A constante de Stefan–Boltzmann σ também aparece nos dois lados e, portanto, também se cancela:
L / L☉ = (R²T⁴) / (R☉²T☉⁴)
Podemos então separar os termos:
L / L☉ = (R² / R☉²) · (T⁴ / T☉⁴)
E utilizar uma propriedade básica das potências:
a² / b² = (a/b)²
e
a⁴ / b⁴ = (a/b)⁴
Assim chegamos finalmente a:
L / L☉ = (R / R☉)² · (T / T☉)⁴
A vantagem é enorme: todos os valores absolutos e constantes que seriam necessários para calcular a luminosidade em watts desaparecem da equação. O resultado passa a ser uma razão adimensional. Se o resultado for:
L / L☉ = 1
a estrela possui a mesma luminosidade do Sol.
L / L☉ = 10
ela possui dez vezes a luminosidade solar. Se for:
L / L☉ = 0,5
ela possui metade da luminosidade solar.
Cara leitor, eu comecei a escrever esse projeto em 11 de junho. Parei de fazer questões de economia e me voltei somente para criar essa estrela. Até entender minimamente o que estava acontecendo, demorou até 29 de agosto. Eu tentei me privas bastante do uso de IA para a confecção desse código, mas nessa parte de cálculo, confesso que usei bastante.
É exatamente essa forma normalizada que foi implementada:
public double calculateLuminositySolar(Star star) {
double radiusRatio = star.getRadiusSolar();
double temperatureRatio = star.getTemperatureKelvin() / PhysicalConstants.SUN_TEMPERATURE_KELVIN;
return Math.pow(radiusRatio, 2) * Math.pow(temperatureRatio, 4);
}A primeira linha importante é:
double radiusRatio = star.getRadiusSolar();O método getRadiusSolar() não está retornando o raio em quilômetros ou metros. Ele representa o raio da estrela em unidades solares:
radiusRatio = R / R☉Assim, se:
radiusRatio = 1a estrela possui o mesmo raio do Sol. Se:
radiusRatio = 2ela possui o dobro do raio solar. Se:
radiusRatio = 0.5ela possui metade do raio solar. Depois calculamos a razão entre a temperatura da estrela e a temperatura do Sol:
double temperatureRatio = star.getTemperatureKelvin() / PhysicalConstants.SUN_TEMPERATURE_KELVIN;Isso corresponde diretamente a:
temperatureRatio = T / T☉Finalmente, o retorno:
return Math.pow(radiusRatio, 2) * Math.pow(temperatureRatio, 4);é simplesmente a implementação direta da equação:
L / L☉ = (R / R☉)² · (T / T☉)⁴O mais legal é que o código possui uma diferença tão grande da fórmula física, e isso deve permanecer para outras situações.
Usar o Sol como referência também torna o resultado muito mais conveniente para o restante da simulação. Em vez de trabalhar com números absolutos extremamente grandes em watts, posso trabalhar com uma escala em que o próprio Sol vale aproximadamente:
**1 L☉**Assim, a luminosidade passa a ser uma propriedade relativa e fácil de interpretar.
Uma estrela com:
**L = 0,01 L☉**é cem vezes menos luminosa que o Sol.
Uma estrela com:
**L = 100 L☉**é cem vezes mais luminosa. Isso também torna mais simples comparar estrelas de diferentes tamanhos e temperaturas sem carregar constantes físicas desnecessárias por toda a aplicação.
Para realizar essa normalização, preciso de uma temperatura solar de referência. Estou utilizando 5772 K, valor nominal adotado pela União Astronômica Internacional na Resolução B3 de 2015. Esse valor também aparece no cálculo da temperatura efetiva do Sol apresentado neste artigo acadêmico em português.
Portanto, no código:
PhysicalConstants.SUN_TEMPERATURE_KELVINrepresenta:
**T☉ = 5772 K**e não um valor aleatório escolhido para a simulação. No fim, toda a regra pode ser resumida em uma cadeia bastante direta:
**raio + temperatura → área superficial + emissão por área → luminosidade**
Ou, matematicamente:
**R e T → 4πR² · σT⁴ → L**
e, na forma utilizada pela simulação:
**R/R☉ e T/T☉ → (R/R☉)² · (T/T☉)⁴ → L/L☉**
Essa é a razão pela qual a luminosidade não precisa ser sorteada. Dadas as propriedades físicas da estrela, sua luminosidade é determinada pela relação entre seu raio e sua temperatura.
Com a luminosidade em mãos, dá para calcular a zona habitável. Novamente utilizei IA para entender a aproximação derivada do trabalho de Kasting, Whitmire & Reynolds "Habitable Zones around Main Sequence Stars”, que aparentemente é o paper fundador dessa área na astronomia moderna. Para entender o conceito lendo em português, usei a Wikipédia em português sobre Zona Habitável e a dissertação Determinação da zona habitável de exoplanetas (UNESP):
d_interno (UA) = √(L / 1.1)
d_externo (UA) = √(L / 0.53)
public double calculateInnerBoundaryAu(Star star) {
return Math.sqrt(star.getLuminositySolar() / INNER_EDGE_FLUX_FACTOR); // 1.1
}
public double calculateOuterBoundaryAu(Star star) {
return Math.sqrt(star.getLuminositySolar() / OUTER_EDGE_FLUX_FACTOR); // 0.53
}Para uma estrela com a luminosidade do Sol (L = 1 L☉), isso dá algo entre 0,95 e 1,37 UA, o que bate com o que a literatura considera a zona habitável do próprio Sistema Solar visto que a Terra está a 1 UA, bem no meio.
Todo esse sistema só faz sentido se for determinístico: a mesma seed sempre precisa produzir a mesma estrela. Eu quero criar um universo reproduzível (pelo menos nessa parte). Por isso, em nenhum lugar do código aparece Math.random() ou new Random() sem uma seed explícita vinda de fora. Toda função recebe um RandomGenerator, interface introduzida na JEP 356: Enhanced Pseudo-Random Number Generators, do Java 17 que eu já expliquei mais a fundo aqui…
var random = new Random(42L);
var star = new StarGenerator().generate("Sol", random);Rode esse trecho hoje, rode de novo daqui a um ano, em outra máquina, em outro sistema operacional e a estrela vai sair idêntica, porque a seed 42L sempre produz a mesma sequência de números dentro do Random.
Star{
id=4f083ce3-f12b-bb4b-46ee-9d82b52c856d,
name='Sol',
spectralType=M,
massSolar=0.33,
radiusSolar=0.51,
temperatureKelvin=3393,
luminositySolar=0.03
}
Eu chamei a estrela de "Sol", mas a seed 42 decidiu que ela é, na verdade, uma anã vermelha.
Pedi para o GPT gerar um texto descritivo como seria uma tarde nesse planeta e uma imagem sobre essa estrela.
No auge do verão, o céu permanecia sob a luz avermelhada de uma pequena estrela. O Sol parecia uma brasa distante, tingindo o horizonte de cobre e deixando o céu em tons profundos de azul e violeta. Sua luz era suave, mas suficiente para aquecer o solo e fazer a vegetação escura brilhar em tons de vermelho e púrpura.
Era uma tarde quente, mas diferente daquelas de um mundo banhado por uma estrela como o nosso Sol. As sombras permaneciam longas, o ar carregava o cheiro das plantas aquecidas e, acima de tudo, aquela pequena estrela vermelha dominava o céu — fraca para os padrões cósmicos, mas o coração daquele mundo.
Eu não quero que a lógica de negocio fique sobre responsabilidade de outra coisa e se que tem várias pessoas por aí que podem se beneficiar para criar seus próprios simuladores, portanto esse é meu primeiro projeto publicado no Maven Central. Estou testando seu uso e sei que tem muitos pontos de melhoria, mas ainda estou trabalhando nisso com o tempo. Para usar no seu projeto com Maven é só:
<dependency>
<groupId>io.github.navelogic</groupId>
<artifactId>genesis-core</artifactId>
<version>0.1.0</version>
</dependency>import io.github.navelogic.genesis.core.astronomy.star.model.Star;
import io.github.navelogic.genesis.core.astronomy.star.service.StarGenerator;
import io.github.navelogic.genesis.core.astronomy.star.service.HabitableZoneCalculator;
import java.util.Random;
var random = new Random(42L);
var star = new StarGenerator().generate("Sol", random);
System.out.println(star);
var zone = new HabitableZoneCalculator();
System.out.printf("Zona habitável: %.2f a %.2f UA%n",
zone.calculateInnerBoundaryAu(star), zone.calculateOuterBoundaryAu(star));O gerador de estrelas é só a primeira peça, a camada mais alta da pilha, como falei no começo. O próximo passo é usar a luminosidade e a zona habitável para gerar planetas plausíveis e só então voltar para a simulação econômica que me trouxe até aqui. Claro que todos esses dados podem servir para uma LLM gerar coisas muito interessantes na simulação. Principalmente para descritivos, livros, filmes, preferencias de produtos e etc.
O código do Genesis é aberto, então se você quiser ver a implementação completa, sugerir uma correção ou simplesmente saber o que vem a seguir.
Estrelas e classificação espectral
- Stars: Crash Course Astronomy #26
- The Hertzsprung-Russell Diagram — NAAP (University of Nebraska-Lincoln)
- O Diagrama HR — Instituto de Física, UFRGS
- Diagrama de Hertzsprung-Russell — Wikipédia
Anãs vermelhas e formação estelar
- Anãs vermelhas e vida no Universo — O Universo: Mistérios Revelados, HISTORY
- Quais são os tipos de estrelas anãs: o que você precisa saber — National Geographic Brasil
- Formação Estelar — Instituto de Física, UFRGS
Lei de Stefan-Boltzmann e temperatura do Sol
- Lei de Stefan–Boltzmann — Wikipédia
- Extremos de luminosidade, raios e densidades — Instituto de Física, UFRGS
- Cálculo da temperatura efetiva do Sol através da Lei de Stefan-Boltzmann: uma revisão teórica
- IAU 2015 Resolution B3 on Recommended Nominal Conversion Constants (valor de 5772 K)
Zona habitável
- Kasting, J. F.; Whitmire, D. P.; Reynolds, R. T. (1993). Habitable Zones around Main Sequence Stars. Icarus, 101(1), 108–128. DOI 10.1006/icar.1993.1010
- Zona habitável — Wikipédia
- Determinação da zona habitável de exoplanetas — dissertação, UNESP
Java
O projeto