Skip to content

Commit

Permalink
Merge pull request #294 from fusion-energy/develop
Browse files Browse the repository at this point in the history
Adding GUI
  • Loading branch information
shimwell authored Jan 13, 2023
2 parents 60f4465 + 6afc0e1 commit 1653074
Show file tree
Hide file tree
Showing 5 changed files with 328 additions and 5 deletions.
71 changes: 71 additions & 0 deletions .devcontainer/gui.Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# This Dockerfile creates a paramak Graphical User Interface that forms part
# of the xsplot.com webapps
#
# There are build args availalbe for specifying the:
# - cq_version
# The version of CadQuery to use master or 2.1
# Default is 2.1
# Options: [master, 2, 2.1]
#
# Example builds:
# Building using the defaults (cq_version master)
# docker build -t paramak_gui .
#
# Building to include cadquery master.
# Run command from within the base repository directory
# docker build -t paramak_gui --build-arg cq_version=master .
#
# Once build the dockerimage can be run in a few different ways.
#
# Run with the following command for a jupyter notebook interface
# docker run -p 8050:8050 paramak_gui


FROM continuumio/miniconda3:4.9.2 as dependencies
#
# By default this Dockerfile builds with the latest release of CadQuery 2
ARG cq_version=master

ENV LANG=C.UTF-8 LC_ALL=C.UTF-8 \
DEBIAN_FRONTEND=noninteractive

RUN apt-get --allow-releaseinfo-change update
RUN apt-get update -y && \
apt-get upgrade -y

RUN apt-get install -y libgl1-mesa-glx libgl1-mesa-dev libglu1-mesa-dev freeglut3-dev libosmesa6 libosmesa6-dev libgles2-mesa-dev curl imagemagick && \
apt-get clean

# Installing CadQuery and Gmsh
RUN echo installing CadQuery version $cq_version && \
conda install -c conda-forge -c python python=3.8 && \
conda install -c conda-forge -c cadquery cadquery="$cq_version" && \
conda install -c conda-forge moab && \
conda install -c conda-forge gmsh && \
conda install -c conda-forge python-gmsh && \
pip install jupyter-cadquery && \
conda clean -afy


FROM dependencies as install

ARG paramak_version=develop

RUN mkdir paramak
COPY src paramak/src/
COPY pyproject.toml paramak/pyproject.toml

COPY README.md paramak/README.md
COPY LICENSE.txt paramak/LICENSE.txt

RUN cd paramak && \
SETUPTOOLS_SCM_PRETEND_VERSION_FOR_PARAMAK=${paramak_version} pip install .[gui]

ENV PORT 8501

EXPOSE 8501

# solves bug of streamlit not running in container
# https://github.com/streamlit/streamlit/issues/4842
ENTRYPOINT [ "streamlit", "run" ]
CMD [ "paramak/src/paramak/gui/app.py", "--server.headless", "true", "--server.fileWatcherType", "none", "--browser.gatherUsageStats", "false"]
7 changes: 7 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,16 @@ docs = [
"sphinx_autodoc_typehints",
"sphinxcadquery"
]
gui = [
"streamlit>=1.14.1",
"streamlit-image-select>=0.5.1",
]

[tool.black]
line-length = 120

[tool.setuptools]
package-dir = {"" = "src"}

[project.scripts]
paramak = "paramak.gui.launch:main"
229 changes: 229 additions & 0 deletions src/paramak/gui/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
import os
from pathlib import Path

import paramak
import streamlit as st
from streamlit_image_select import image_select

st.set_page_config(
page_title="Paramak",
page_icon="⚛",
layout="wide",
)

hide_streamlit_style = """
<style>
#MainMenu {visibility: hidden;}
footer {
visibility: hidden;
}
</style>
"""
st.markdown(hide_streamlit_style, unsafe_allow_html=True)
st.markdown("# Paramak GUI")
st.markdown(
"## A Graphical User Interface to [Paramak](https://github.com/fusion-energy/paramak) "
"for parametric fusion reactor geometry creation"
)

# FIXME get images names from https://paramak.readthedocs.io/en/main/API-Reference.html#parametric-reactors
reactor_image_links = [
"",
"https://user-images.githubusercontent.com/8583900/211223396-41ed8628-5352-4e7a-8c4a-97914174954e.png",
"https://user-images.githubusercontent.com/8583900/99136724-91af6f00-261e-11eb-9956-476b818a0ee3.png",
"https://user-images.githubusercontent.com/8583900/99136728-983de680-261e-11eb-8398-51ae433f5546.png",
"https://user-images.githubusercontent.com/8583900/99136727-94aa5f80-261e-11eb-965d-0ccceb2743fc.png",
"https://user-images.githubusercontent.com/8583900/99136719-8e1be800-261e-11eb-907d-a9bafaebdbb8.png",
"https://user-images.githubusercontent.com/8583900/99136731-9aa04080-261e-11eb-87a5-502708dfebcc.png",
"https://user-images.githubusercontent.com/8583900/99136734-9e33c780-261e-11eb-837b-16a0bc59f8a7.png",
"https://user-images.githubusercontent.com/8583900/110224418-4f62b400-7ed3-11eb-85f1-e40dc74f5671.png",
"https://user-images.githubusercontent.com/40028739/110248118-cf3e5c00-7f6f-11eb-9e68-864c1a1e8676.png",
"https://user-images.githubusercontent.com/8583900/100032191-5ae01280-2def-11eb-9654-47c3869b3a2c.png",
"https://user-images.githubusercontent.com/85617935/144303187-8cb71e2d-fc35-450f-a8f4-88b6650d56b7.png",
]

reactor_names = [
"0",
"FlfSystemCodeReactor",
"BallReactor",
"SingleNullBallReactor",
"SegmentedBlanketBallReactor",
"SubmersionTokamak",
"SingleNullSubmersionTokamak",
"CenterColumnStudyReactor",
"EuDemoFrom2015PaperDiagram",
"IterFrom2020PaperDiagram",
"SparcFrom2020PaperDiagram",
"NegativeTriangularityReactor",
]


selected_reactor_index = image_select(
label="Select a reactor",
images=reactor_image_links,
captions=reactor_names,
use_container_width=False,
return_value="index", # function returns index instead of image
index=0, # initial selected image is 0 which is blank
)

# gets the caption name of the selected image
selected_reactor = reactor_names[selected_reactor_index]

st.write(selected_reactor)
write_cad_buttons = True
col1, col2, col3 = st.columns([1, 1, 1])

if selected_reactor_index not in [0, None]:

if selected_reactor == "FlfSystemCodeReactor":
inner_blanket_radius = col1.number_input("inner blanket radius", value=100.0)
blanket_thickness = col1.number_input("blanket thickness", value=70.0)
blanket_height = col1.number_input("blanket height", value=500.0)
lower_blanket_thickness = col1.number_input("lower blanket thickness", value=50.0)
upper_blanket_thickness = col2.number_input("upper blanket thickness", value=40.0)
blanket_vv_gap = col2.number_input("blanket vv gap", value=20.0)
upper_vv_thickness = col2.number_input("upper vv thickness", value=10.0)
vv_thickness = col3.number_input("vv thickness", value=10.0)
lower_vv_thickness = col3.number_input("lower vv thickness", value=10.0)
rotation_angle = col3.number_input("rotation angle", value=180.0)

elif selected_reactor == "BallReactor":
inner_bore_radial_thickness = col1.number_input("inner_bore_radial_thickness", value=10.0)
inboard_tf_leg_radial_thickness = col1.number_input("inboard_tf_leg_radial_thickness", value=30.0)
center_column_shield_radial_thickness = col1.number_input("center_column_shield_radial_thickness", value=60.0)
divertor_radial_thickness = col1.number_input("divertor_radial_thickness", value=150.0)
inner_plasma_gap_radial_thickness = col1.number_input("inner_plasma_gap_radial_thickness", value=30.0)
plasma_radial_thickness = col1.number_input("plasma_radial_thickness", value=300.0)
outer_plasma_gap_radial_thickness = col1.number_input("outer_plasma_gap_radial_thickness", value=30.0)
firstwall_radial_thickness = col1.number_input("firstwall_radial_thickness", value=30.0)
blanket_radial_thickness = col2.number_input("firstwall_radial_thickness", value=50.0)
blanket_rear_wall_radial_thickness = col2.number_input("blanket_rear_wall_radial_thickness", value=30.0)
elongation = col2.number_input("elongation", value=2.0)
triangularity = col2.number_input("triangularity", value=0.55)
plasma_gap_vertical_thickness = col2.number_input("plasma_gap_vertical_thickness", value=50.0)
divertor_to_tf_gap_vertical_thickness = col2.number_input("divertor_to_tf_gap_vertical_thickness", value=0.0)
number_of_tf_coils = col2.number_input("number_of_tf_coils", value=12)
rear_blanket_to_tf_gap = col2.number_input("rear_blanket_to_tf_gap", value=0.0)
pf_coil_radial_thicknesses = col3.text_input(
"pf_coil_radial_thicknesses", value="50,40,50", key="input_pf_coil_radial_thicknesses"
)
pf_coil_vertical_thicknesses = col3.text_input("pf_coil_vertical_thicknesses", value="60,40,60", key="")
pf_coil_radial_position = col3.text_input(
"pf_coil_radial_position", value="500,550,500", key="input_pf_coil_radial_position"
)
pf_coil_vertical_position = col3.text_input(
"pf_coil_vertical_position", value="-250,0,250", key="input_pf_coil_vertical_position"
)
pf_coil_case_thicknesses = col3.text_input(
"pf_coil_case_thicknesses", value="5,5,5", key="input_pf_coil_case_thicknesses"
)
outboard_tf_coil_radial_thickness = col3.number_input(
"outboard_tf_coil_radial_thickness", value=50, key="input_outboard_tf_coil_radial_thickness"
)
outboard_tf_coil_poloidal_thickness = col3.number_input(
"outboard_tf_coil_radial_thickness", value=50, key="input_outboard_tf_coil_poloidal_thickness"
)
divertor_position = col3.selectbox("divertor_position", options=["both", "lower", "upper"])
rotation_angle = col3.number_input("rotation angle", value=180.0)
else:
write_cad_buttons = False
st.write(f"{selected_reactor} not implemented in GUI yet. Contributions are welcome")

if write_cad_buttons:
with st.spinner("Building the 3d model"):
if selected_reactor == "FlfSystemCodeReactor":
paramak_reactor = paramak.FlfSystemCodeReactor(
inner_blanket_radius=inner_blanket_radius,
blanket_thickness=blanket_thickness,
blanket_height=blanket_height,
lower_blanket_thickness=lower_blanket_thickness,
upper_blanket_thickness=upper_blanket_thickness,
blanket_vv_gap=blanket_vv_gap,
upper_vv_thickness=upper_vv_thickness,
vv_thickness=vv_thickness,
lower_vv_thickness=lower_vv_thickness,
rotation_angle=rotation_angle,
)
elif selected_reactor == "BallReactor":
paramak_reactor = paramak.BallReactor(
inner_bore_radial_thickness=inner_bore_radial_thickness,
inboard_tf_leg_radial_thickness=inboard_tf_leg_radial_thickness,
center_column_shield_radial_thickness=center_column_shield_radial_thickness,
divertor_radial_thickness=divertor_radial_thickness,
inner_plasma_gap_radial_thickness=inner_plasma_gap_radial_thickness,
plasma_radial_thickness=plasma_radial_thickness,
outer_plasma_gap_radial_thickness=outer_plasma_gap_radial_thickness,
firstwall_radial_thickness=firstwall_radial_thickness,
blanket_radial_thickness=blanket_radial_thickness,
blanket_rear_wall_radial_thickness=blanket_rear_wall_radial_thickness,
elongation=elongation,
triangularity=triangularity,
plasma_gap_vertical_thickness=plasma_gap_vertical_thickness,
divertor_to_tf_gap_vertical_thickness=divertor_to_tf_gap_vertical_thickness,
number_of_tf_coils=number_of_tf_coils,
rear_blanket_to_tf_gap=rear_blanket_to_tf_gap,
pf_coil_radial_thicknesses=[float(v) for v in pf_coil_radial_thicknesses.split(",")],
pf_coil_vertical_thicknesses=[float(v) for v in pf_coil_vertical_thicknesses.split(",")],
pf_coil_radial_position=[float(v) for v in pf_coil_radial_position.split(",")],
pf_coil_vertical_position=[float(v) for v in pf_coil_vertical_position.split(",")],
pf_coil_case_thicknesses=[float(v) for v in pf_coil_case_thicknesses.split(",")],
outboard_tf_coil_radial_thickness=outboard_tf_coil_radial_thickness,
outboard_tf_coil_poloidal_thickness=outboard_tf_coil_poloidal_thickness,
divertor_position=divertor_position,
rotation_angle=rotation_angle,
)

save_path = Path(os.path.realpath(__file__)).parent

save_html_file = save_path / "reactor.html"
paramak_reactor.export_html_3d(save_html_file)
with open(save_html_file, "r", encoding="UTF-8") as file1:
html_data = file1.read()

save_stp_file = save_path / "reactor.stp"
paramak_reactor.export_stp(str(save_stp_file))
with open(save_stp_file, "r", encoding="UTF-8") as file2:
stp_data = file2.read()

# save_stl_file = save_path / "reactor.stl"
# paramak_reactor.export_stl(str(save_stl_file))
# with open(save_stl_file, "r") as file3:
# stl_data = file3.read()

# FIXME fix so that it works
# save_h5m_file = save_path / "reactor.h5m"
# paramak_reactor.export_dagmc_h5m(str(save_h5m_file))
# with open(save_h5m_file, "r") as file4:
# h5m_data = file4.read()

col1_buttons, col2_buttons = st.columns([1, 1])

# FIXME see if on_click arg can be used to make stp file on demand
# https://docs.streamlit.io/library/api-reference/widgets/st.download_button
col1_buttons.download_button(
"Download CAD (STP format)",
stp_data,
file_name="paramak.stp",
)
col2_buttons.download_button(
"Download CAD (HTML format)",
html_data,
file_name="paramak.html",
)

# this is not currently working, perhaps due to binary files
# col2_buttons.download_button(
# "Download CAD (STL format)",
# stl_data,
# file_name="paramak.stl",
# )

# FIXME fix so that it works
# st.download_button(
# "Download DAGMC (h5m format)",
# stl_data,
# file_name="paramak.h5m",
# )

st.components.v1.html(html_data, height=800)
17 changes: 17 additions & 0 deletions src/paramak/gui/launch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import runpy
import sys
from pathlib import Path

import paramak


def main():

path_to_app = str(Path(paramak.__path__[0]) / "gui" / "app.py")

sys.argv = ["streamlit", "run", path_to_app]
runpy.run_module("streamlit", run_name="__main__")


if __name__ == "__main__":
main()
9 changes: 4 additions & 5 deletions src/paramak/parametric_reactors/ball_reactor.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,12 @@ class BallReactor(paramak.Reactor):
divertor_to_tf_gap_vertical_thickness: the vertical thickness of the
gap between the divertor and the TF coils.
number_of_tf_coils: the number of tf coils
rear_blanket_to_tf_gap: the radial distance between the back of the
blankets and the start of the TF coils.
pf_coil_radial_thicknesses: the radial
thickness of each poloidal field coil.
pf_coil_vertical_thicknesses: the vertical
thickness of each poloidal field coil.
pf_coil_to_tf_coil_radial_gap: the radial distance
between the rear of the poloidal field coil and the toroidal field
coil.
pf_coil_radial_position: The radial (x) position(s) of the centers of
the poloidal field coils.
pf_coil_vertical_position: The vertical (z) position(s) of the centers
Expand All @@ -69,13 +68,13 @@ def __init__(
inner_plasma_gap_radial_thickness: float = 30.0,
plasma_radial_thickness: float = 300.0,
outer_plasma_gap_radial_thickness: float = 30.0,
plasma_gap_vertical_thickness: float = 50.0,
firstwall_radial_thickness: float = 30.0,
blanket_radial_thickness: float = 50.0,
blanket_rear_wall_radial_thickness: float = 30.0,
elongation: float = 2.0,
triangularity: float = 0.55,
divertor_to_tf_gap_vertical_thickness: Optional[float] = 0,
plasma_gap_vertical_thickness: float = 50.0,
divertor_to_tf_gap_vertical_thickness: Optional[float] = 0.0,
number_of_tf_coils: Optional[int] = 12,
rear_blanket_to_tf_gap: Optional[float] = None,
pf_coil_radial_thicknesses: List[float] = [],
Expand Down

0 comments on commit 1653074

Please sign in to comment.