# Author: Andrew Louwagie Gordon
# Date Created: 13Jun2018
# Last Modified: 03Jan2019
# Import Block
# Import the necessary packages
import ipywidgets as widgets
import bqplot as bq
import numpy as np
import tempNcolor as tc
import number_formatting as nf
from IPython.display import display
import pythreejs as p3j
# Function Definitions Block
def find_flux(l, r):
"""
This takes the luminosity of the star and the distance from Earth to calculate flux.
Parameters
----------
l : float
The luminosity of the star in Solar Luminosities.
r : float
The distance the star is from the observer in Parsecs squared.
Returns
-------
flux : float
The flux of the star at the observers location in Solar Luminosities per Parsec squared.
"""
flux = l / (4 * np.pi * (r ** 2))
return flux
def w(change=None): # w stands for widgets
'''
This function continuously updates the widgets that display information as well as the opacity of the star
in the figure.
'''
distance_pc = Dist_PC.value
luminosity = L_given_R(Rad.value)
# Use luminosity and radius to estimate temperature
Temp = pow(luminosity/(Rad.value*Rad.value), 0.25)*5777
hexcolor = tc.rgb2hex(tc.temp2rgb(Temp))[0]
flux_sl_pc2 = find_flux(luminosity, distance_pc)
star_size = (Rad.value/1.7) * 1000
Luminosity.value = str(nf.SigFig(luminosity, 3))
Flux.value = str(nf.SigFig(flux_sl_pc2, 3))
scaled_flux = float(flux_sl_pc2) / 0.97
rounded_flux = round(scaled_flux, 1)
star.default_size = int(star_size)
star_earth.default_opacities = [rounded_flux]
star.colors = [hexcolor]
star_earth.colors = [hexcolor]
# the following lines defined a function that maps Radius to Luminosity for Main Sequence Stars.
Lvs_R_coeff = [-14.414732200764211, -13.259918928247322, 88.94347154444976, -24.088776726372608,
-87.71861646290176, 31.910048976800123, 30.045785739826723, -10.017454060166651,
-1.7461666512820873, 5.534581622863519, -0.06725347697088192]
LogLum = np.poly1d(Lvs_R_coeff)
# Construct a log_L(log_R) function using previously fit 10th order polynomial fit to Appendix G data
def L_given_R(radius):
# Set some constants
logR_min = -0.70
logR_max = 1.13
logR = np.log10(radius)
if (logR>logR_max) or (logR<logR_min):
print("You may be outside validity range of luminosity model.")
return 10**LogLum(logR)
# Variable Definitions Block
# Initialize the distance.
distance_pc = 1
# Initialize the luminosity and convert it.
luminosity = 12.2 # In Solar Luminosities.
# Calculate flux in Solar luminosities per parsec squared.
flux_sl_pc2 = find_flux(luminosity, distance_pc)
# Widgets Definitions Block
# This is the distance from Earth. Units of parsecs.
Dist_PC = widgets.FloatSlider(
min=1.0,
value=1.0,
max=5.0,
step=0.1,
disabled=False,
continuous_update=True,
orientation='horizontal',
readout=True,
readout_format='.1f',
)
Rad = widgets.FloatSlider(
value=1.7,
min=0.2,
max=1.7,
step=0.1,
disabled=False,
readout_format = '.1f'
)
# Widget to report updated luminosity in Watts
Luminosity = widgets.Text(
value = str(nf.SigFig(luminosity, 3)) ,
disabled = True
)
# Widget to report updated flux in Solar Luminosities per parsec squared
Flux = widgets.Text(
value = str(nf.SigFig(flux_sl_pc2, 3)),
disabled = True
)
# Define the distances based on the slider
distance_pc = Dist_PC.value
# Calculate the fluxes
flux_sl_pc2 = find_flux(luminosity, distance_pc)
# Widget Updates Block
# Observe the update functions
Dist_PC.observe(w, names=['value'])
Rad.observe(w, names=['value'])
# Size all of the widgets appropriately
Dist_PC_report = widgets.HBox([widgets.Label ('Distance (Parsecs):'), Dist_PC])
Dist_PC_report.children[0].layout.width = '150px'
Dist_PC_report.children[1].layout.width = '150px'
Rad_report = widgets.HBox([widgets.HTML ('Radius (R<sub>☉</sub>):'), Rad])
Rad_report.children[0].layout.width = '150px'
Rad_report.children[1].layout.width = '150px'
Luminosity_report = widgets.HBox([widgets.HTML ('Luminosity (L<sub>☉</sub>):'), Luminosity])
Luminosity_report.children[0].layout.width = '150px'
Luminosity_report.children[1].layout.width = '150px'
Flux_report = widgets.HBox([widgets.Label ('Flux ($\\frac{L_\odot}{pc^2}$): '), Flux])
Flux_report.children[0].layout.width = '150px'
Flux_report.children[1].layout.width = '150px'
#Figure Update Block
# Observe the update function
Dist_PC.observe(w, names=['value'])
Rad.observe(w, names=['value'])
# The following lines scale the flux to be between 0 and 1 which are then rounded to one decimal places and used as the
# opacity arguement for the figure.
scaled_flux = float(flux_sl_pc2) / 0.97
rounded_flux = round(scaled_flux, 1)
# These are the parameters for the visual plot
x_arr = np.linspace(1,10,num=100)
y_arr = x_arr
sc_x = bq.LinearScale()
sc_y = bq.LinearScale()
# Use luminosity and radius to estimate temperature
Temp = pow(L_given_R(Rad.value)/(Rad.value*Rad.value), 0.25)*5777
hexcolor = tc.rgb2hex(tc.temp2rgb(Temp))[0]
# Generate the star in the figure
star = bq.Scatter(x=[5.5], y=[5.5], scales={'x': sc_x, 'y': sc_y}, names = ['Star'], colors = [hexcolor],
default_size = 1000, default_opacities = [1.0])
star_earth = bq.Scatter(x=[5.5], y=[5.5], scales={'x': sc_x, 'y': sc_y}, names = ['Star'], colors = [hexcolor],
default_size = 20, default_opacities = [rounded_flux])
lin = bq.Scatter(x=x_arr, y=y_arr, scales={'x': sc_x, 'y': sc_y}, opacites=[0])
ax_x = bq.Axis(scale=sc_x, grid_color = 'black', num_ticks = 0)
ax_y = bq.Axis(scale=sc_y, grid_color = 'black', num_ticks = 0, tick_format='0.2f', orientation='vertical')
# Make the figure
lum_fig = bq.Figure(title = 'View from 1 AU Away', marks=[star], axes=[ax_x, ax_y], animation = 1000, min_aspect_ratio = 1,
max_aspect_ratio = 1, background_style ={'fill':'black'})
brightness_fig = bq.Figure(title = 'Telescopic View from Earth', marks=[star_earth], axes=[ax_x, ax_y], animation = 1000, min_aspect_ratio = 1,
max_aspect_ratio = 1, background_style ={'fill':'black'})
# Set scale factor for radius (10 pixels per solar radius)
scale_factor = 1
# Set viewer size
view_width = 400
view_height = 400
star2 = p3j.Mesh(geometry=p3j.SphereBufferGeometry(0.375, 32, 16),
material=p3j.MeshBasicMaterial(color = 'white'),
position=[0, 0, 10])
plate1 = p3j.Mesh(geometry = p3j.BoxBufferGeometry(3, 3, 0.01),
material = p3j.MeshBasicMaterial(color='white', transparent=True, opacity=1),
position=[0, 0, 5])
plate2 = p3j.Mesh(geometry = p3j.BoxBufferGeometry(6, 6, 0.01),
material = p3j.MeshBasicMaterial(color='white', transparent=True, opacity=1/4),
position=[0, 0, 0])
plate3 = p3j.Mesh(geometry = p3j.BoxBufferGeometry(9, 9, 0.01),
material = p3j.MeshBasicMaterial(color='white', transparent=True, opacity=1/9),
position=[0, 0, -5])
plate4 = p3j.Mesh(geometry = p3j.BoxBufferGeometry(12, 12, 0.01),
material = p3j.MeshBasicMaterial(color='white', transparent=True, opacity=1/16),
position=[0, 0, -10])
line2v = p3j.Mesh(geometry = p3j.BoxBufferGeometry(0.1, 6, 0.01),
material = p3j.MeshBasicMaterial(color='black'),
position=[0, 0, 0])
line2h = p3j.Mesh(geometry = p3j.BoxBufferGeometry(6, 0.1, 0.01),
material = p3j.MeshBasicMaterial(color='black'),
position=[0, 0, 0])
line3v1 = p3j.Mesh(geometry = p3j.BoxBufferGeometry(0.1, 9, 0.01),
material = p3j.MeshBasicMaterial(color='black'),
position=[0.66, 0, -5])
line3v2 = p3j.Mesh(geometry = p3j.BoxBufferGeometry(0.1, 9, 0.01),
material = p3j.MeshBasicMaterial(color='black'),
position=[-0.66, 0, -5])
line3h1 = p3j.Mesh(geometry = p3j.BoxBufferGeometry(9, 0.1, 0.01),
material = p3j.MeshBasicMaterial(color='black'),
position=[0, 0.5, -5])
line3h2 = p3j.Mesh(geometry = p3j.BoxBufferGeometry(9, 0.1, 0.01),
material = p3j.MeshBasicMaterial(color='black'),
position=[0, -0.5, -5])
line4v1 = p3j.Mesh(geometry = p3j.BoxBufferGeometry(0.1, 12, 0.01),
material = p3j.MeshBasicMaterial(color='black'),
position=[1, 0, -10])
line4v2 = p3j.Mesh(geometry = p3j.BoxBufferGeometry(0.1, 12, 0.01),
material = p3j.MeshBasicMaterial(color='black'),
position=[0, 0, -10])
line4v3 = p3j.Mesh(geometry = p3j.BoxBufferGeometry(0.1, 12, 0.01),
material = p3j.MeshBasicMaterial(color='black'),
position=[-1, 0, -10])
line4h1 = p3j.Mesh(geometry = p3j.BoxBufferGeometry(12, 0.1, 0.01),
material = p3j.MeshBasicMaterial(color='black'),
position=[0, 1, -10])
line4h2 = p3j.Mesh(geometry = p3j.BoxBufferGeometry(12, 0.1, 0.01),
material = p3j.MeshBasicMaterial(color='black'),
position=[0, 0, -10])
line4h3 = p3j.Mesh(geometry = p3j.BoxBufferGeometry(12, 0.1, 0.01),
material = p3j.MeshBasicMaterial(color='black'),
position=[0, -1, -10])
# Define viewing region size.
xmax=1.5*10
# Makes the scene environment.
scene2 = p3j.Scene(children=[star2, plate1, plate2, plate3, plate4, line2v, line2h, line3v1, line3v2, line3h1, line3h2,
line4v1, line4v2, line4v3, line4h1, line4h2, line4h3,p3j.AmbientLight(color='white')],
background='black')
# Creates the camera so you can see stuff. Place the cemera just above the x-axis and orient camera so up
# is along y-axis.
starcam = p3j.PerspectiveCamera(position=[2*xmax, 10, 30], up=[0, 1, 0])
# Makes a controller to use for the scene.
controller = p3j.OrbitControls(controlling=starcam, enableRotate=False, enableZoom=False)
# Creates the object that gets displayed to the screen.
renderer2 = p3j.Renderer(camera=starcam,
scene=scene2,
controls=[controller],
width=view_width, height=view_height)
box_layout = widgets.Layout(align_items='center', align_content = 'center', border='none', width='100%',
justify_content = 'flex-end')
fig = widgets.VBox([widgets.Label ("Dispersion of Light:"), renderer2], layout = box_layout)
Welcome to the Brightness-Luminosity Simulation. Using the inverse square law we know that flux (apparent brightness) is proportional to luminosity and inversly proporional to the distance away squared. This is shown in the following figure which displays how the light spreads out over distance as it gets farther from the source.
display(fig)
In the figure below, use the distance slider and the luminosity box to give the model main sequence star a distance (in parsecs) and a radius (in Solar radii). The radius us used to figure out the luminosity of a main sequence star of that radius (in solar luminosities). Examine what happens to the star in the figure. Which value has a greatest effect on the visibility of the star?
# Display Block
# Organize the widgets presentably
b_layout = widgets.Layout(align_items='center', align_content = 'center', border='none', justify_content = 'center',
width = '100%')
box_top = widgets.HBox([lum_fig, brightness_fig], layout = b_layout)
left_box = widgets.VBox([Dist_PC_report, Rad_report])
right_box = widgets.VBox([Luminosity_report, Flux_report])
box_bottom = widgets.HBox([left_box, right_box], layout = b_layout)
big_box = widgets.VBox([box_top, box_bottom], layout = b_layout)
display(big_box)