Interactive web-based user interface for the CMB-S4 reference simulation tool

See the Documentation

If your browser doesn't visualize the widget input boxes, try reloading the page and disable your adblocker.

For support requests, open an issue on the s4_design_sim_tool repository

import ipywidgets as widgets
from IPython.display import display
w = {}
for emission in ["foreground_emission", "CMB_unlensed", "CMB_lensing_signal"]:
    w[emission] = widgets.BoundedFloatText(
    value=1,
    min=0,
    max=1,
    step=0.01,
    description='Weight:',
        disabled=False
    )
emission = "CMB_tensor_to_scalar_ratio"
w[emission] = widgets.BoundedFloatText(
    value=3e-3,
    min=0,
    max=1,
    step=1e-5,
    description=f'r:',
        disabled=False
    )

Sky emission weighting

Each sky emission has a weighting factor between 0 and 1

Foreground emission

Synchrotron, Dust, Free-free, AME Websky CIB, tSZ, kSZ

display(w["foreground_emission"])

Unlensed CMB

Planck cosmological parameters, no tensor modes

display(w["CMB_unlensed"])

CMB lensing signal

CMB lensed - CMB unlensed:

  • 1 for lensed CMB
  • 0 for unlensed CMB
  • >0, <1 for residual after de-lensing

For the case of partial de-lensing, consider that lensing is a non-linear and this is a very rough approximation, still it could be useful in same cases, for example low-ell BB modes.

display(w["CMB_lensing_signal"])

CMB tensor to scalar ratio

Value of the r cosmological parameter

display(w["CMB_tensor_to_scalar_ratio"])

Experiment parameters

Total experiment length

In years, supports decimals

w["total_experiment_length_years"] = widgets.BoundedFloatText(
    value=7,
    min=0,
    max=15,
    step=0.1,
    description='Years:',
        disabled=False
    )
display(w["total_experiment_length_years"])
w["observing_efficiency"] = widgets.BoundedFloatText(
    value=0.2,
    min=0,
    max=1,
    step=0.01,
    description='Efficiency:',
        disabled=False
    )

Observing efficiency

Typically 20%, use decimal notation

display(w["observing_efficiency"])
w["number_of_splits"] = widgets.BoundedIntText(
    value=1,
    min=1,
    max=7,
    step=1,
    description='Splits:',
        disabled=False
    )

Number of splits

Number of splits, 1 generates only full mission 2-7 generates the full mission map and then the requested number of splits scaled accordingly. E.g. 7 generates the full mission map and 7 equal (yearly) maps

display(w["number_of_splits"])

Telescope configuration

Currently we constraint to have a total of 6 SAT and 3 LAT, each SAT has a maximum of 3 tubes, each LAT of 19. The checkbox on the right of each telescope checks that the amount of number of tubes is correct.

import toml
config = toml.load("s4_design.toml")
def define_check_sum(telescope_widgets, max_tubes):
    def check_sum(_):
        total_tubes = sum([w.value for w in telescope_widgets[1:1+4]])
        telescope_widgets[0].value = total_tubes == max_tubes
    return check_sum
telescopes = {"SAT":{}, "LAT":{}}
for telescope, tubes in config["telescopes"]["SAT"].items():
    telescopes["SAT"][telescope] = [widgets.Valid(
    value=True, description=telescope, layout=widgets.Layout(width='120px')
    )]
    telescope_check = define_check_sum(telescopes["SAT"][telescope], 3)
    for k,v in tubes.items():
        if k == "site":
            wid = widgets.Dropdown(
            options=['Pole', 'Chile'],
            value=v,
            description=k,
            disabled=False, layout=widgets.Layout(width='150px')
            )
        elif k == "years":
            wid = widgets.BoundedFloatText(
    value=v,
    min=0,
    max=20,
    step=0.1,
    description='years',
        disabled=False, layout=widgets.Layout(width='130px')
    )
        else:
            
            wid = widgets.BoundedIntText(
    value=v,
    min=0,
    max=3,
    step=1,
    description=k,
    disabled=False, layout=widgets.Layout(width='130px')
            ) 
            wid.observe(telescope_check)
        telescopes["SAT"][telescope].append(wid)
for k, v in telescopes["SAT"].items():
    display(widgets.HBox(v))
for telescope, tubes in config["telescopes"]["LAT"].items():
    telescopes["LAT"][telescope] = [widgets.Valid(
    value=True, description=telescope, layout=widgets.Layout(width='120px')
    )]
    telescope_check = define_check_sum(telescopes["LAT"][telescope], 19)
    for k,v in tubes.items():
        if k == "site":
            wid = widgets.Dropdown(
            options=['Pole', 'Chile'],
            value=v,
            description=k,
            disabled=False, layout=widgets.Layout(width='150px')
            )
        elif k == "years":
            wid = widgets.BoundedFloatText(
    value=v,
    min=0,
    max=20,
    step=0.1,
    description='years',
        disabled=False, layout=widgets.Layout(width='130px')
    )
        else:
            
            wid = widgets.BoundedIntText(
    value=v,
    min=0,
    max=19,
    step=1,
    description=k,
    disabled=False, layout=widgets.Layout(width='130px')
            ) 
            wid.observe(telescope_check)
        telescopes["LAT"][telescope].append(wid)
for k, v in telescopes["LAT"].items():
    display(widgets.HBox(v))
import toml
toml_decoder = toml.decoder.TomlDecoder()
toml_encoder = toml.TomlPreserveInlineDictEncoder()

def generate_toml():
    output_config = {}
    for section in ["sky_emission", "experiment"]:
        output_config[section] = {}
        for k in config[section]:
            output_config[section][k] = w[k].value
    output_config["telescopes"] = {"SAT":{}, "LAT":{}}
    for t in ["SAT", "LAT"]:
        for telescope, tubes in telescopes[t].items():
            output_config["telescopes"][t][telescope] = toml_decoder.get_empty_inline_table()
            for tube_type in tubes[1:]:
                output_config["telescopes"][t][telescope][tube_type.description] = tube_type.value
                if tube_type.description == "years":
                    output_config["telescopes"][t][telescope][tube_type.description] = int(tube_type.value)
    return toml.dumps(output_config, encoder=toml_encoder)
from s4_design_sim_tool.cli import md5sum_string, S4RefSimTool
from pathlib import Path

Generate a TOML configuration file

Click on the button to generate the TOML file and display it.

import os
output_location = os.environ.get("S4REFSIMTOOL_OUTPUT_URL", "")
button = widgets.Button(
    description='Generate TOML',
    disabled=False,
    button_style='info', # 'success', 'info', 'warning', 'danger' or ''
    tooltip='Click me',
    icon='check'
)
output_label = widgets.HTML(value="")
output = widgets.Output(layout={'border': '1px solid black'})

display(button, output_label, output)

def on_button_clicked(b):
    output.clear_output()
    
    toml_string = generate_toml()
    md5sum = md5sum_string(toml_string)

    output_path = Path("output") / md5sum
    output_label.value = ""
    
    if output_path.exists():
        output_label.value = "This exact CMB-S4 configuration has <b>already been executed</b><br />" + \
        f"<a href='{output_location}/output/{md5sum}' target='blank'><button class='p-Widget jupyter-widgets jupyter-button widget-button mod-success'>Access the maps </button></a>"
    output_label.value += "<p>TOML file preview:</p>"
    
    with output:
        print(toml_string)

button.on_click(on_button_clicked)
import ipywidgets as widgets
import logging

class OutputWidgetHandler(logging.Handler):
    """ Custom logging handler sending logs to an output widget """

    def __init__(self, *args, **kwargs):
        super(OutputWidgetHandler, self).__init__(*args, **kwargs)
        layout = {
            'width': '100%',
            'height': '500px',
            'border': '1px solid black'
        }
        self.out = widgets.Output(layout=layout)

    def emit(self, record):
        """ Overload of logging.Handler method """
        formatted_record = self.format(record)
        new_output = {
            'name': 'stdout',
            'output_type': 'stream',
            'text': formatted_record+'\n'
        }
        self.out.outputs = (new_output, ) + self.out.outputs

    def show_logs(self):
        """ Show the logs """
        display(self.out)

    def clear_logs(self):
        """ Clear the current logs """
        self.out.clear_output()


logger = logging.root
handler = OutputWidgetHandler()
handler.setFormatter(logging.Formatter('%(asctime)s  - [%(levelname)s] %(message)s'))
logger.addHandler(handler)
logger.setLevel(logging.INFO)

Run the simulation

Generate the output maps

create_wget_script[source]

create_wget_script(folder, output_location)

def run_simulation(toml_filename, md5sum):
    output_path = toml_filename.parents[0]
    sim = S4RefSimTool(toml_filename, output_folder=output_path)
    sim.run(channels="all", sites=["Pole", "Chile"])
    
    logger.info("Create the wget script")
    create_wget_script(output_path, output_location)
run_button = widgets.Button(
    description='Create the maps',
    disabled=False,
    button_style='danger', # 'success', 'info', 'warning', 'danger' or ''
    tooltip='Click me',
    icon='check'
)

run_output_label = widgets.HTML(value="")

handler.clear_logs()
display(run_button, run_output_label, handler.out)

def on_run_button_clicked(_):
    run_button.disabled = True

    toml_string = generate_toml()
    md5sum = md5sum_string(toml_string)

    output_path = Path("output") / md5sum
    
    if output_path.exists():
        logger.error("This configuration has already been executed")
        run_button.disabled = False
        return
    
    output_path.mkdir(parents=True, exist_ok=True)
    toml_filename = output_path / "config.toml"

    with open(toml_filename, "w") as f:
        f.write(toml_string)
        
    run_output_label.value = "<p> The simulation has been launched, see the logs below, access the TOML configuration file and the maps as they are created clicking on the button </p>" + \
        f"<a href='{output_location}/output/{md5sum}' target='blank'><button class='p-Widget jupyter-widgets jupyter-button widget-button mod-success'>Access the maps </button></a>"
 
    run_simulation(toml_filename, md5sum)
    run_button.disabled = False
    

run_button.on_click(on_run_button_clicked)