def __init__(self, name, primary, secondary, avgsep, ep=0, es=0, id=None): if secondary.mass <= primary.mass: self.primary = primary self.secondary = secondary else: self.primary = secondary self.secondary = primary if name is None: self.has_name = False self.name = 'NoName' else: self.name = name self.ecc_p = q(ep) self.ecc_s = q(es) self.average_separation = q(avgsep, 'au') self.barycenter = q(avgsep * (self.secondary.mass.m / (self.primary.mass.m + self.secondary.mass.m)), 'au') self.primary_distance = round(self.barycenter, 2) self.secondary_distance = round(self.average_separation - self.primary_distance, 2) self.primary.orbit = BinaryStarOrbit(self.primary, self.secondary, self.primary_distance, self.ecc_p) self.secondary.orbit = BinaryStarOrbit(self.secondary, self.primary, self.secondary_distance, self.ecc_s) self.system_name = self.__repr__() # ID values make each system unique, even if they have the same stars and separation. now = ''.join([char for char in str(datetime.now()) if char not in [' ', '.', ':', '-']]) self.id = id if id is not None else now
def __init__(self, primary, secondary, avgsep, ep=0, es=0, id=None, name=None): super().__init__(name, primary, secondary, avgsep, ep, es, id=id) assert 0.4 <= ep <= 0.7, 'Primary eccentricity must be between 0.4 and 0.7' max_sep_p, min_sep_p = self.calculate_distances(ep, self.primary_distance.m) assert 0.4 <= es <= 0.7, 'Secondary eccentricity must be between 0.4 and 0.7' max_sep_s, min_sep_s = self.calculate_distances(es, self.secondary_distance.m) self.max_sep = max_sep_p + max_sep_s self.min_sep = min_sep_p + min_sep_s assert self.min_sep.m > 0.1, "Stars will merge at {:~} minimum distance".format(self.min_sep) self._mass = primary.mass + secondary.mass self._luminosity = primary.luminosity + secondary.luminosity self._habitable_inner = round(sqrt(self._luminosity.m / 1.1), 3) self._habitable_outer = round(sqrt(self._luminosity.m / 0.53), 3) self._inner_boundry = round(self._mass.m * 0.01, 3) self._outer_boundry = round(self._mass.m * 40, 3) self._frost_line = round(4.85 * sqrt(self._luminosity.m), 3) self.set_qs() self.spin = self.primary.spin self.inner_forbbiden_zone = q(round(self.min_sep.m / 3, 3), 'au') self.outer_forbbiden_zone = q(round(self.max_sep.m * 3, 3), 'au') self.habitable_orbit = round(self.max_sep * 4, 3)
def reset_period_and_speed(self, main_body_mass): satellite_mass = round(self.astrobody.mass.m, 3) self.velocity = q(sqrt(main_body_mass.m / self._a), 'earth_orbital_velocity').to('kilometer per second') self.period = q( sqrt( pow(self.a.to('au').m, 3) / (main_body_mass.m + satellite_mass)), 'year').to('day')
def tilt(self, value): if 0 <= value < 90: self.spin = 'prograde' elif 90 <= value <= 180: self.spin = 'retrograde' self._tilt = q(value, 'degree') self.habitable = self.set_habitability() self.albedo = self.albedo if not self.habitable else q(29)
def __init__(self, data): mass = data.get('mass', False) radius = data.get('radius', False) gravity = data.get('gravity', False) if not mass and not radius and not gravity: raise AssertionError('must specify at least two values') name = data.get('name', None) if name is not None: self.name = name self.has_name = True unit = data.get('unit', "earth") self.unit = unit self.idx = data.get('idx', 0) self._mass = None if not mass else mass self._radius = None if not radius else radius self._gravity = None if not gravity else gravity self._temperature = 0 if not self._gravity: self._gravity = mass / pow(radius, 2) if not self._radius: self._radius = sqrt(mass / gravity) if not self._mass: self._mass = gravity * pow(radius, 2) self.set_qs(unit) if 'composition' in data and data['composition'] is not None: self.composition = { 'water ice': data['composition'].get('water ice', 0), 'silicates': data['composition'].get('silicates', 0), 'iron': data['composition'].get('iron', 0), 'helium': data['composition'].get('helium', 0), 'hydrogen': data['composition'].get('hydrogen', 0), } self.atmosphere = {} self.albedo = q(data['albedo']) self.greenhouse = q( data['greenhouse']) if 'greenhouse' in data else q(1) self.clase = data['clase'] if 'clase' in data else self.set_class( self.mass, self.radius) self.tilt = data['tilt'] if 'tilt' in data else 0 self.satellites = [] # ID values make each planet unique, even if they have the same characteristics. now = ''.join([ char for char in str(datetime.now()) if char not in [' ', '.', ':', '-'] ]) self.id = data['id'] if 'id' in data else now self.system_id = data.get('system', None)
def __truediv__(self, other): new_a = None if type(other) not in (float, Orbit): return NotImplemented() elif type(other) is float: new_a = q(self.semi_major_axis.m / other, self._unit) elif type(other) is Orbit: new_a = q(self.semi_major_axis.m / other.semi_major_axis.m, self._unit) return new_a.m
def elevate_changes(key, new_value): if key == 'Eccentricity': if new_value.m < 0.001: return q(0.001) elif new_value.m > 0.9999: return q(0.999) elif key == 'Inclination': if new_value.m < 0: return q(0, new_value.u) elif new_value.m > 180: return q(180, new_value.u)
def __init__(self, primary, secondary, avgsep, ep=0, es=0, id=None, name=None): super().__init__(name, primary, secondary, avgsep, ep, es, id=id) assert 0.4 <= ep <= 0.7, 'Primary eccentricity must be between 0.4 and 0.7' max_sep_p, min_sep_p = self.calculate_distances(ep, self.average_separation.m) assert 0.4 <= es <= 0.7, 'Secondary eccentricity must be between 0.4 and 0.7' max_sep_s, min_sep_s = self.calculate_distances(es, self.average_separation.m) self.max_sep = max_sep_p + max_sep_s self.min_sep = min_sep_p + min_sep_s self.inner_forbbiden_zone = q(self.min_sep.m / 3, 'au') self.outer_forbbiden_zone = q(self.max_sep.m * 3, 'au')
def update(self): self.reset_power() if hasattr(self.value, '__round__') and self.unit is not None: if self.parent.do_round: value = q(add_decimal(str(round(self.value, 3))), self.unit) else: value = q(add_decimal(str(self.value)), self.unit) if self.unit != 'year': value = '{:~}'.format(value) value = str(value) else: value = str(self.value) self.image = self.f.render(value, True, self.fg, self.bg) self.rect = self.image.get_rect(topleft=self.rect.topleft)
def set_qs(self, unit): m = unit + '_mass' r = unit + '_radius' self.mass = q(self._mass, m) self.radius = q(self._radius, r) self.gravity = q(self._gravity, unit + '_gravity') self.density = self.calculate_density(self.mass.to('grams'), self.radius.to('centimeters')) self.volume = self.calculate_volume(self.radius.to('kilometers')) self.surface = self.calculate_surface_area( self.radius.to('kilometers')) self.circumference = self.calculate_circumference( self.radius.to('kilometers')) self.escape_velocity = q(sqrt(self.mass.m / self.radius.m), unit + '_escape_velocity')
def temp_by_lat(lat) -> q: """Devuelve la temperatura media relativa a la latitud seleccionada :rtype: q """ if type(lat) is q: lat = lat.m if lat > 90: lat -= 90 elif lat < 0: lat = abs(lat) temp = None if 0 <= lat <= 10: temp = -(5 * lat / 9) + 33 elif 11 <= lat <= 37: temp = -(9 * lat / 26) + 31 elif 38 <= lat <= 60: temp = -(17 * lat / 24) + 44 elif 61 <= lat <= 75: a = ((lat / 60) - 3) b = (lat - 60) temp = a * b if b != 0 else 0.0 elif 76 <= lat <= 90: temp = -lat + 45 if temp is not None: return q(round(temp, 2), 'celsius') else: raise ValueError('La latitud "{}" no es válida'.format(lat))
def set_hill_sphere(self): a = self.orbit.semi_major_axis.to('au').magnitude mp = self.mass.to('earth_mass').magnitude ms = self.orbit.star.mass.to('sol_mass').magnitude # "star" is actually the planet in this case return q(round((a * pow(mp / ms, 1 / 3)), 3), 'earth_hill_sphere').to('earth_radius')
def set_orbital_properties(inclination): if inclination in (0, 180): return q(0, 'degree'), 'undefined' else: values = rotation_loop() Renderer.reset() return values
def show_mass(self): try: mass = Systems.get_current().get_available_mass() except AttributeError: mass = q(0, 'jupiter_mass') if not self.show_jovian_mass: mass = mass.to('earth_mass') attr = '{:,g~}'.format((round(mass, 4))) return attr
def set_roche(self, obj_density): density = self.density.to('earth_density').m radius = self.get_radius().to('earth_radius').m roches = q(round(2.44 * radius * pow(density / obj_density, 1 / 3), 3), 'earth_radius') if self.roches_limit == 0 or roches < self.roches_limit: self.roches_limit = roches return self.roches_limit
def create_ring(self, asteroid): mass = asteroid.mass.to('ring_mass').m density = asteroid.density.to('earth_density').m vol = mass / density outer_limit = self.roches_limit.to('km') inner_limit = q(self._radius + 10000, 'km') # "10K" debería ser seteado por el Atmosphere Panel, porque es el fin de la atmósfera. wideness = outer_limit - inner_limit ro = outer_limit.to('earth_radius').m ri = inner_limit.to('earth_radius').m ra = pow((vol / (4 / 3 * pi)), 3) thickness = q(4 / 3 * (pow(ra, 3) / (pow(ro, 2) - pow(ri, 2))), 'km').to('m') return Ring(self, inner_limit, outer_limit, wideness, thickness)
def set_qs(self): self.mass = q(self._mass.m, 'sol_mass') self.luminosity = q(self._luminosity.m, 'sol_luminosity') self.habitable_inner = q(self._habitable_inner, 'au') self.habitable_outer = q(self._habitable_outer, 'au') self.inner_boundry = q(self._inner_boundry, 'au') self.outer_boundry = q(self._outer_boundry, 'au') self.frost_line = q(self._frost_line, 'au')
def set_loaded_orbits(self): for orbit_data in self._loaded_orbits: a = q(orbit_data['a'], 'au') if 'e' not in orbit_data: self.add_orbit_marker(a) else: e = q(orbit_data['e']) i = q(orbit_data['i'], 'degree') system = Systems.get_system_by_id(orbit_data['star_id']) planet = system.get_astrobody_by(orbit_data['astrobody'], tag_type='id') star = system.star_system planet.set_orbit(star, [a, e, i]) self.add_orbit_marker(planet.orbit) self.planet_area.delete_objects(planet) # borrar las órbitas cargadas para evitar que se dupliquen. self.sort_markers() self._loaded_orbits.clear()
def tune_value(self, delta): if not self.locked: self.increment = self.update_increment() self.increment *= delta if Systems.get_current_star().validate_orbit(self.value.m + self.increment): self.value += q(self.increment, self.value.u) self.increment = 0 self.parent.sort_markers()
def fill(self, tos): for i, elemento in enumerate(self.relatives.widgets()): arg = self.relative_args[i] if self.parent.relative_mode: got_attr = getattr(self.current, arg) else: got_attr = getattr(self.current, arg).to(tos[arg]) attr = q(str(round(got_attr.m, 5)), got_attr.u) if type(got_attr) is not str else got_attr elemento.value = attr elemento.text_area.show() for i, elemento in enumerate(self.absolutes.widgets()): arg = self.absolute_args[i] got_attr = getattr(self.current, arg) attr = q(str(round(got_attr.m, 3)), got_attr.u) if type(got_attr) is not str else got_attr elemento.value = attr elemento.text_area.show() self.has_values = True
def from_stellar_resonance(star, planet, resonance: str): """Return the semi-major axis in AU of the resonant orbit. The resonance argument is a string in the form of 'x:y' where both x and y are integers and x >= y. """ x, y = [int(i) for i in resonance.split(':')] period = (x / y) * planet.orbit.period.to('year').m semi_major_axis = q( pow(pow(period, 2) * star.mass.to('sol_mass').m, (1 / 3)), 'au') return semi_major_axis
def calculate_density(ice, silicate, iron): comp = { 'water ice': ice / 100, 'silicates': silicate / 100, 'iron': iron / 100 } density = q( sum([ comp[material] * material_densities[material] for material in comp ]), 'g/cm^3') return density
def fill(self): parametros = [] for elemento in self.properties.widgets(): if elemento.text == 'Inclination': value = q( 0 if elemento.text_area.value == '' else elemento.text_area.value, 'degree') elif elemento.text not in ['Orbital motion', 'Temperature']: value = q(elemento.text_area.value) else: value = 'au' parametros.append(value) main = self.parent.current orbit = self.linked_astrobody.set_orbit(main, parametros) self.linked_marker.orbit = orbit self.show() self.parent.planet_area.delete_objects(self.linked_astrobody) if hasattr(self.parent, 'recomendation'): self.parent.recomendation.hide() self.locked = True self.has_values = True
def set_loaded_orbits(self): for orbit_data in self._loaded_orbits: a = q(orbit_data['a'], 'earth_radius') e = q(orbit_data['e']) i = q(orbit_data['i'], 'degree') system = Systems.get_system_by_id(orbit_data['star_id']) planet = system.get_astrobody_by(orbit_data['planet_id'], tag_type='id') if planet.id not in self.satellites: self.satellites[planet.id] = [] if planet.id not in self._markers: self._markers[planet.id] = [] satellite = system.get_astrobody_by(orbit_data['astrobody'], tag_type='id') self.satellites[planet.id].append(satellite) satellite.set_orbit(planet, [a, e, i]) self.add_existing(satellite, planet.id) # borrar las órbitas cargadas para evitar que se dupliquen. self._loaded_orbits.clear()
def from_planetary_resonance(planet, satellite, resonance: str): """Return the semi-major axis in Re of the resonant orbit. The resonance argument is a string in the form of 'x:y' where both x and y are integers and x >= y. """ x, y = [int(i) for i in resonance.split(':')] period = (x / y) * satellite.orbit.period.to('year').m mass = planet.mass.m + satellite.mass.m # earth masses semi_major_axis = q(pow(pow(period, 2) * mass, (1 / 3)), 'au') return semi_major_axis.to('earth_radius')
def lagrange_three(sma, m_primary, m_secondary, ref='primary'): delta_a_limit, m_primary, m_secondary, sma, period_secondary = _lagrange(sma, m_primary, m_secondary) a = 1 delta_a = 1000000000000 while delta_a > delta_a_limit: condition = True while condition: b = _acc(3, a, m_primary, m_secondary, sma, period_secondary) c = _acc(3, a + delta_a, m_primary, m_secondary, sma, period_secondary) a = a + delta_a condition = abs(b) > abs(c) a = a - 2 * delta_a delta_a /= 10 secondary_distance = -(a + sma) # in meters primary_distance = a # in meters assert ref == 'primary' or ref == 'secondary', 'Requested options are invalid' if ref == 'primary': return q(primary_distance, 'm') elif ref == 'secondary': return q(secondary_distance, 'm')
def __init__(self, star_system): self.planets = [] self.satellites = [] self.asteroids = [] self.astro_bodies = [] self.star_system = star_system self.id = star_system.id self.body_mass = q( 16 * exp(-0.6931 * star_system.mass.m) * 0.183391347289428, 'jupiter_mass') self.aparent_brightness = {} self.average_visibility = {}
def load_atmosphere(self, event): if 'Planets' in event.data and len(event.data['Planets']): atmosphere = event.data['Planets'][0]['atmosphere'] elements = [e.symbol for e in self.elements.widgets()] for elem in atmosphere: if elem != 'pressure_at_sea_level': idx = elements.index(elem) element = self.elements.widgets()[idx] element.percent.value = str(atmosphere[elem]) else: value = atmosphere[elem]['value'] unit = atmosphere[elem]['unit'] self.pressure = q(value, unit) self.pre_loaded = True
def set_class(mass, radius): em = 'earth_mass' jm = 'jupiter_mass' jr = 'jupiter_radius' if q(0.0001, em) < mass < q(0.1, em) and radius > q( 0.03, 'earth_radius'): return 'Dwarf Planet' elif q(10, em) < mass < q(13, jm): return 'Gas Giant' elif mass < q(2, jm) and radius > q(1, jr): return 'Puffy Giant' else: raise AssertionError("couldn't class the planet")
def fill(self): tos = {'Mass': 'Me', 'Density': 'De', 'Volume': 'Ve'} for elemento in self.properties.get_widgets_from_layer(2): idx = self.properties.widgets().index(elemento) attr = self.relative_args[idx] if not self.parent.relative_mode: got_attr = getattr(self.current, attr) else: got_attr = getattr(self.current, attr).to(tos[elemento.text.capitalize()]) attr = q(str(got_attr.m), got_attr.u) if type(got_attr) is not str else got_attr elemento.value = attr if elemento.text_area.unit == 'earth_mass': elemento.do_round = False elemento.text_area.show() for elemento in self.properties.get_widgets_from_layer(3): name = elemento.text if ' ' in elemento.text: name = name.replace(' ', '_') got_attr = getattr(self.current, name.lower()) attr = q(str(round(got_attr.m, 3)), got_attr.u) if type(got_attr) is not str else got_attr elemento.value = attr elemento.text_area.show() for elemento in self.properties.get_widgets_from_layer(4): got_attr = self.current.composition.get(elemento.text.lower(), 0) attr = str(round(got_attr, 3)) + ' %' elemento.value = attr elemento.text_area.show() self.has_values = True