├── .gitignore ├── CITATION.cff ├── Dockerfile ├── LICENSE ├── README.md ├── app.py ├── assets ├── android-chrome-192x192.png ├── android-chrome-512x512.png ├── apple-touch-icon.png ├── favicon-16x16.png ├── favicon-32x32.png ├── favicon.ico ├── site.webmanifest └── typography.css └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | 131 | # openmc cross section files 132 | *.h5 133 | 134 | # test nuclear data 135 | *_json/ 136 | convert.py 137 | download.py 138 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | cff-version: 1.2.0 2 | message: "If you use this software, please cite it as below." 3 | authors: 4 | - family-names: "Shimwell" 5 | given-names: "Jonathan" 6 | orcid: "https://orcid.org/0000-0001-6909-0946" 7 | - family-names: "Delaporte-Mathurin" 8 | given-names: "Rémi" 9 | orcid: "https://orcid.org/0000-0003-1064-8882" 10 | title: "XSPlot - Neutron cross section plotter for isotopes" 11 | version: 0.0.5 12 | date-released: 2021-8-6 13 | url: "https://github.com/openmc-data-storage/isotope-xs-plotter" 14 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | # build with 3 | # sudo docker build -t xsplot . 4 | 5 | # run with 6 | # sudo docker run --network host -t xsplot 7 | 8 | # maintained at https://github.com/openmc-data-storage/xsplot.com/ 9 | 10 | FROM ghcr.io/openmc-data-storage/nuclear_data_base_docker 11 | 12 | # the base image nuclear_data_base_docker is based on continuumio/miniconda3:4.9.2 13 | 14 | RUN pip install dash 15 | 16 | RUN pip install gunicorn==20.0.4 17 | 18 | COPY assets assets 19 | COPY app.py . 20 | 21 | ENV PORT 8080 22 | 23 | EXPOSE 8080 24 | 25 | # Run the web service on container startup. Here we use the gunicorn 26 | # webserver, with one worker process and 8 threads. 27 | # For environments with multiple CPU cores, increase the number of workers 28 | # to be equal to the cores available. 29 | # Timeout is set to 0 to disable the timeouts of the workers to allow Cloud Run 30 | # to handle instance scaling. For more details see 31 | # https://cloud.google.com/run/docs/quickstarts/build-and-deploy/python 32 | CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 app:server 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 openmc-data-storage 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This repository contains part of the source code for neutron cross section 2 | plotting website [xsplot.com](http://xsplot.com) which allows neutron cross 3 | sections for isotopes to be plotted. 4 | 5 | This repository contains: 6 | - A Python [Plotly Dash](https://plotly.com/dash/) based GUI 🐍 7 | - A Dockerfile that provides the hosting environment 🐳 8 | 9 | # Run locally 10 | 11 | You can view the hosted version of this repository here [xsplot.com](http://xsplot.com). However you might want to host your own version locally. 12 | 13 | To host your own local version of [xsplot.com](http://xsplot.com) you will need [Docker](https://www.docker.com/) installed and then can build and run the Dockerfile 14 | with the following commands. 15 | 16 | First clone the repository 17 | ```bash 18 | git clone https://github.com/openmc-data-storage/isotope-xs-plotter.git 19 | ``` 20 | 21 | Then navigate into the repository folder 22 | ```bash 23 | cd isotope-xs-plotter 24 | ``` 25 | 26 | Then build the docker image 27 | ```bash 28 | docker build -t isotope-xs-plotter . 29 | ``` 30 | 31 | Then run the docker image 32 | ```bash 33 | docker run --network host -t isotope-xs-plotter 34 | ``` 35 | 36 | The URL of your locally hosted version should appear in the terminal, copy and paste this URL into a web browser address bar. 37 | 38 | 39 | # Maintenance 40 | 41 | Pushing to the main branch of this repository triggers an automatic rebuild and 42 | deployment of the new code using Google Cloud build at the [xsplot.com](http://xsplot.com) 43 | 44 | The website makes use of a few other packages to process the nuclear data: 45 | - [openmc_data_downloader](https://github.com/openmc-data-storage/openmc_data_downloader) to download processed h5 nuclear data files. 46 | - [openmc_data_to_json](https://github.com/openmc-data-storage/openmc_data_to_json) to convert the h5 files (per isotope) into seperate reaction files. 47 | - [nuclear_data_base_docker](https://github.com/openmc-data-storage/nuclear_data_base_docker) to provide a dockerfile containing all the required nuclear data with an index / loop up file containing every reaction available. 48 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | from json import dumps, load 2 | 3 | import dash 4 | from dash.dependencies import Input, Output 5 | 6 | from dash import html 7 | from dash import dcc 8 | 9 | # todo update imports dash_table 10 | from dash import dash_table 11 | from pandas import read_hdf 12 | 13 | df = read_hdf("all_indexes.h5", "/data/d1") 14 | 15 | downloaded_xs_data = {} 16 | 17 | 18 | app = dash.Dash( 19 | __name__, 20 | prevent_initial_callbacks=True, 21 | meta_tags=[ 22 | # A description of the app, used by e.g. 23 | # search engines when displaying search results. 24 | {"name": "title", "content": "XSPlot neutron cross section plotter"}, 25 | { 26 | "name": "description", 27 | "content": "Online graph plotting tool for neutron cross sections from a range of nuclear data including TENDL ENDF", 28 | }, 29 | { 30 | "name": "keywords", 31 | "keywords": "plot neutron nuclear cross section energy barns database plotter tendl endf", 32 | }, 33 | {"name": "author", "content": "Jonathan Shimwell"}, 34 | # A tag that tells Internet Explorer (IE) 35 | # to use the latest renderer version available 36 | # to that browser (e.g. Edge) 37 | {"http-equiv": "X-UA-Compatible", "content": "IE=edge"}, 38 | # A tag that tells the browser not to scale 39 | # desktop widths to fit mobile screens. 40 | # Sets the width of the viewport (browser) 41 | # to the width of the device, and the zoom level 42 | # (initial scale) to 1. 43 | # 44 | # Necessary for "true" mobile support. 45 | {"name": "viewport", "content": "width=device-width, initial-scale=1.0"}, 46 | ], 47 | ) 48 | app.title = "XSPlot \U0001f4c9 neutron cross section plotter \U0001f4c8" 49 | app.description = "Plot neutron cross sections. Nuclear data from the TENDL library." 50 | # TODO add description, current google says Dash in description area 51 | # https://github.com/plotly/dash/blob/1a40162dfce654b885e475ecb280d3cca9bff0a5/dash/dash.py#L193 52 | 53 | # added to allow Gunicorn access to Dash Flask as discussed here 54 | # https://ldnicolasmay.medium.com/deploying-a-free-dash-open-source-app-from-a-docker-container-with-gunicorn-3f426b5fd5df 55 | server = app.server 56 | 57 | components = [ 58 | # guide on plotly html https://dash.plotly.com/dash-html-components 59 | html.Title("xsplot.com isotope cross section plotting"), 60 | html.Iframe( 61 | src="https://ghbtns.com/github-btn.html?user=openmc-data-storage&repo=isotope-xs-plotter&type=star&count=true&size=large", 62 | width="170", 63 | height="30", 64 | title="GitHub", 65 | style={"border": 0, "scrolling": "0"}, 66 | ), 67 | html.H1( 68 | "XSPlot - Neutron cross section plotter for isotopes", 69 | # TODO find a nicer font 70 | # style={'font-family': 'Times New Roman, Times, serif'}, 71 | # style={'font-family': 'Georgia, serif'}, 72 | style={"text-align": "center"}, 73 | ), 74 | html.Div( 75 | html.Iframe( 76 | src="https://www.youtube.com/embed/aWXS9AqSkEk", 77 | width="560", 78 | height="315", 79 | title="Tutorial video", 80 | # style={}, 81 | style={"text-align": "center", "border": 0, "scrolling": "0"}, 82 | ), 83 | style={"text-align": "center"}, 84 | ), 85 | html.Div( 86 | [ 87 | html.H3( 88 | [ 89 | "\U0001f50e Search the cross sections database using any of the table headings. \U0001f50d", 90 | ], 91 | style={'text-align': 'center'} 92 | ), 93 | html.H3( 94 | [ 95 | "Make use of logical expressions to refine the database filtering \U0001f449 = < > " 96 | # "Make use of \U0001f449 ", A("MT reaction number", href="https://t2.lanl.gov/nis/endf/mts.html"), 97 | # " or other table headings and then select neutron cross sections to plot. ", 98 | # "Use logical expressions = < > to perform advanced filtering." 99 | ], 100 | style={'text-align': 'center'} 101 | ), 102 | html.H3( 103 | [ 104 | "\U0000269b Make use of standard MT numbers to identify reactions \U0001f449 ", 105 | html.A("reaction descriptions \U0001f517",href="https://t2.lanl.gov/nis/endf/mts.html") , 106 | # " or other table headings and then select neutron cross sections to plot. ", 107 | # "Use logical expressions = < > to perform advanced filtering." 108 | ], 109 | style={'text-align': 'center'} 110 | ), 111 | html.H3( 112 | [ 113 | '\U0001f4c8 The plot should update automatically \U0001f389' 114 | ], 115 | style={'text-align': 'center'} 116 | ), 117 | html.H3( 118 | [ 119 | '\U0001f4c9 Customise you graph and download your cross section data \U0001f4be' 120 | ], 121 | style={'text-align': 'center'} 122 | ), 123 | ] 124 | ), 125 | dash_table.DataTable( 126 | id="datatable-interactivity", 127 | columns=[ 128 | {"name": i, "id": i, "selectable": True} 129 | for i in df.columns 130 | if i not in ["Temperature(K)", "Incident particle"] 131 | ], 132 | data=df.to_dict("records"), 133 | editable=False, 134 | filter_action="native", # TODO change to equals instead of contains 135 | sort_action="native", 136 | sort_mode="multi", 137 | row_selectable="multi", 138 | row_deletable=False, 139 | selected_columns=[], 140 | selected_rows=[], 141 | page_action="native", 142 | page_current=0, 143 | page_size=15, 144 | style_cell={'fontSize':16, 'font-family':'sans-serif'}, 145 | ), 146 | html.Table( 147 | [ 148 | html.Tr( 149 | [ 150 | html.Th( 151 | html.Button( 152 | "clear selection", 153 | title="Clear all selected data. You can also temporarily hide plots by clicked them in the legend", 154 | id="clear", 155 | ) 156 | ), 157 | html.Th( 158 | dcc.RadioItems( 159 | options=[ 160 | {"label": "log X axis", "value": "log"}, 161 | {"label": "linear X axis", "value": "linear"}, 162 | ], 163 | value="log", 164 | id="xaxis_scale", 165 | labelStyle={"display": "inline-block"}, 166 | ), 167 | ), 168 | html.Th( 169 | # html.H5("X axis range (comma delimitated)"), 170 | html.Div(title='Enter both X lower and X upper limit to use, works best on linear X scale.', children=[ 171 | dcc.Input( 172 | id="x_lower_limit", 173 | type='text', 174 | placeholder='X axis lower limit in eV' 175 | ), 176 | ]), 177 | ), 178 | html.Th( 179 | html.Div(title='Enter both X lower and X upper limit to use, works best on linear X scale.', children=[ 180 | dcc.Input( 181 | id="x_upper_limit", 182 | type='text', 183 | placeholder='X axis upper limit in eV' 184 | ), 185 | ]), 186 | ), 187 | ] 188 | ), 189 | html.Tr([html.Br()]), 190 | html.Tr( 191 | [ 192 | html.Th( 193 | html.Button( 194 | "Download Plotted Data", 195 | title="Download a text file of the data in JSON format", 196 | id="btn_download2", 197 | ) 198 | ), 199 | html.Th( 200 | dcc.RadioItems( 201 | options=[ 202 | {"label": "log Y axis", "value": "log"}, 203 | {"label": "linear Y axis", "value": "linear"}, 204 | ], 205 | value="log", 206 | id="yaxis_scale", 207 | ), 208 | ), 209 | # TODO add slider from SO 210 | # https://stackoverflow.com/questions/61896144/dash-range-slider-with-input-on-each-side 211 | html.Th( 212 | html.Div(title='Enter both Y lower and Y upper limit to use, works best on linear Y scale.', children=[ 213 | dcc.Input( 214 | id="y_lower_limit", 215 | type='text', 216 | placeholder='Y axis lower limit in Barns', 217 | ), 218 | ]), 219 | ), 220 | html.Th( 221 | html.Div(title='Enter both Y lower and Y upper limit to use, works best on linear Y scale.', children=[ 222 | dcc.Input( 223 | id="y_upper_limit", 224 | type='text', 225 | placeholder='Y axis upper limit in Barns' 226 | ), 227 | ]), 228 | ), 229 | ] 230 | ), 231 | ], 232 | style={"width": "100%"}, 233 | ), 234 | html.Br(), 235 | dcc.Loading( 236 | id="loading-1", 237 | type="default", 238 | children=html.Div(id="graph_container") 239 | ), 240 | html.H5("X axis units"), 241 | dcc.Slider( 242 | min=0, 243 | max=4, 244 | marks={i: f"{s}" for i, s in enumerate(["μeV", "eV", "keV", "MeV", "GeV"])}, 245 | value=1, 246 | id="x_axis_units", 247 | ), 248 | dcc.Download(id="download-text-index"), 249 | html.Br(), 250 | html.Div( 251 | [ 252 | html.Label("XSPlot is an open-source project powered by "), 253 | html.A("OpenMC", href="https://docs.openmc.org/en/stable/"), 254 | html.Label(", "), 255 | html.A(" Plotly", href="https://plotly.com/"), 256 | html.Label(", "), 257 | html.A(" Dash", href="https://dash.plotly.com/"), 258 | html.Label(", "), 259 | html.A(" Dash datatable", href="https://dash.plotly.com/datatable"), 260 | html.Label(", "), 261 | html.A(" Flask", href="https://flask.palletsprojects.com/en/2.0.x/"), 262 | html.Label(", "), 263 | html.A(" Gunicorn", href="https://gunicorn.org/"), 264 | html.Label(", "), 265 | html.A(" Docker", href="https://www.docker.com"), 266 | html.Label(", "), 267 | html.A(" GCloud", href="https://cloud.google.com"), 268 | html.Label(", "), 269 | html.A(" Python", href="https://www.python.org/"), 270 | html.Label(" with the source code available on "), 271 | html.A(" GitHub", href="https://github.com/openmc-data-storage"), 272 | ], 273 | style={"text-align": "center"}, 274 | ), 275 | html.Br(), 276 | html.Div( 277 | [ 278 | html.Label("Links to alternative cross section plotting websites: "), 279 | html.A("NEA JANIS", href="https://www.oecd-nea.org/jcms/pl_39910/janis"), 280 | html.Label(", "), 281 | html.A(" IAEA ENDF", href="https://www-nds.iaea.org/exfor/endf.htm"), 282 | html.Label(", "), 283 | html.A(" IAEA Libraries", href="https://nds.iaea.org/dataexplorer"), 284 | html.Label(", "), 285 | html.A(" NNDC Sigma", href="https://www.nndc.bnl.gov/sigma/"), 286 | html.Label(", "), 287 | html.A( 288 | " Nuclear Data Center JAEA", 289 | href="https://wwwndc.jaea.go.jp/ENDF_Graph/", 290 | ), 291 | html.Label(", "), 292 | html.A("T2 LANL", href="https://t2.lanl.gov/nis/data/endf/index.html"), 293 | html.Label(", "), 294 | html.A("Nuclear Data Center KAERI", href="https://atom.kaeri.re.kr"), 295 | ], 296 | style={"text-align": "center"}, 297 | ), 298 | ] 299 | 300 | 301 | app.layout = html.Div(components) 302 | 303 | 304 | @app.callback( 305 | Output("datatable-interactivity", "selected_rows"), 306 | Input("clear", "n_clicks"), 307 | ) 308 | def clear(n_clicks): 309 | return [] 310 | 311 | 312 | @app.callback( 313 | Output("datatable-interactivity", "style_data_conditional"), 314 | [Input("datatable-interactivity", "selected_columns")], 315 | ) 316 | def update_styles(selected_columns): 317 | return [ 318 | {"if": {"column_id": i}, "background_color": "#D2F3FF"} 319 | for i in selected_columns 320 | ] 321 | 322 | 323 | def get_uuid_from_row(row): 324 | atomic_symbol = row["Atomic symbol"].to_string(index=False) 325 | mass_number = row["Mass number"].to_string(index=False) 326 | library = row["Library"].to_string(index=False) 327 | incident_particle_symbol = "n" 328 | reaction = row["MT reaction number"].to_string(index=False) 329 | 330 | if library == "TENDL-2019": 331 | temperature = "294K" 332 | else: 333 | temperature = "300K" # FENDL 3.1d 334 | 335 | uuid = "_".join( 336 | [ 337 | atomic_symbol, 338 | mass_number, 339 | library, 340 | incident_particle_symbol, 341 | str(int(reaction)), 342 | str(temperature), 343 | ] 344 | ) 345 | return uuid 346 | 347 | 348 | @app.callback( 349 | Output("graph_container", "children"), 350 | [ 351 | Input("datatable-interactivity", "selected_rows"), 352 | Input("xaxis_scale", "value"), 353 | Input("yaxis_scale", "value"), 354 | Input("x_axis_units", "value"), 355 | Input("x_lower_limit", "value"), 356 | Input("x_upper_limit", "value"), 357 | Input("y_lower_limit", "value"), 358 | Input("y_upper_limit", "value"), 359 | ] 360 | ) 361 | def update_graphs( 362 | selected_rows, 363 | xaxis_scale, 364 | yaxis_scale, 365 | x_axis_units, 366 | x_lower_limit, 367 | x_upper_limit, 368 | y_lower_limit, 369 | y_upper_limit, 370 | ): 371 | # When the table is first rendered, `derived_virtual_data` and 372 | # `selected_rows` will be `None`. This is due to an 373 | # idiosyncracy in Dash (unsupplied properties are always None and Dash 374 | # calls the dependent callbacks when the component is first rendered). 375 | # So, if `rows` is `None`, then the component was just rendered 376 | # and its value will be the same as the component's dataframe. 377 | # Instead of setting `None` in here, you could also set 378 | # `derived_virtual_data=df.to_rows('dict')` when you initialize 379 | # the component. 380 | if selected_rows is None: 381 | selected_rows = [] 382 | 383 | global downloaded_xs_data 384 | 385 | if len(downloaded_xs_data) > 0: 386 | for entry in selected_rows: 387 | if entry in downloaded_xs_data.keys(): 388 | downloaded_xs_data[entry]["plot"] = False 389 | 390 | for entry in selected_rows: 391 | row = df.iloc[[entry]] 392 | 393 | uuid = get_uuid_from_row(row) 394 | library = row["Library"].to_string().split()[1] 395 | 396 | fn = library + "_json/" + uuid + ".json" 397 | with open(fn) as json_file: 398 | xs = load(json_file) 399 | 400 | xs["plot"] = True 401 | xs["legend"] = "{}{} (n,{}) {}".format( 402 | xs["Atomic symbol"], 403 | xs["Mass number"], 404 | xs["Reaction products"], 405 | xs["Library"], 406 | ) 407 | 408 | downloaded_xs_data[entry] = xs 409 | 410 | all_x_y_data = [] 411 | 412 | x_axis_units_multiplier = {0: -3, 1: 0, 2: 3, 3: 6, 4: 9} 413 | for k, v in downloaded_xs_data.items(): 414 | import math 415 | 416 | if k in selected_rows: 417 | 418 | multiplier = math.pow(10, -1 * x_axis_units_multiplier[x_axis_units]) 419 | energy = [x * multiplier for x in downloaded_xs_data[k]["energy"]] 420 | 421 | all_x_y_data.append( 422 | { 423 | "y": downloaded_xs_data[k]["cross section"], 424 | "x": energy, 425 | "type": "scatter", 426 | "name": downloaded_xs_data[k]["legend"] 427 | # "marker": {"color": colors}, 428 | } 429 | ) 430 | 431 | # previous website had more complex unit logic 432 | # https://github.com/Shimwell/database_GUI/blob/d670ca88feef8f41a0f20abd30bdb2a82cbab6bd/src/App.js#L305-L329 433 | x_axis_units_text = {0: "μeV", 1: "eV", 2: "keV", 3: "MeV", 4: "GeV"} 434 | 435 | energy_units = f"[{x_axis_units_text[x_axis_units]}]" 436 | xs_units = "[barns]" 437 | 438 | 439 | if len(selected_rows) != 0: 440 | fig={ 441 | "data": all_x_y_data, 442 | "layout": { 443 | "height":800, 444 | # "width":1600, 445 | "margin": {"l": 3, "r": 2, "t": 15, "b": 60}, 446 | "xaxis": { 447 | "title": {"text": f"Energy {energy_units}"}, 448 | "type": xaxis_scale, 449 | "tickformat": ".1e", 450 | "tickangle": 45, 451 | "rangemode": 'nonnegative' 452 | }, 453 | "yaxis": { 454 | "automargin": True, 455 | "title": {"text": f"Microscopic Cross Section {xs_units}"}, 456 | "type": yaxis_scale, 457 | "tickformat": ".1e", 458 | }, 459 | "showlegend": True, 460 | # "height": 250, 461 | # "margin": {"t": 10, "l": 10, "r": 10}, 462 | }, 463 | } 464 | 465 | if x_upper_limit is not None and x_lower_limit is not None: 466 | try: 467 | float_values = (float(x_lower_limit), float(x_upper_limit)) 468 | fig["layout"]["xaxis"]["range"]=float_values 469 | except: 470 | fig["layout"]["xaxis"]["range"]=None 471 | else: 472 | fig["layout"]["xaxis"]["range"]=None 473 | 474 | if y_upper_limit is not None and y_lower_limit is not None: 475 | try: 476 | float_values = (float(y_lower_limit), float(y_upper_limit)) 477 | fig["layout"]["yaxis"]["range"]=float_values 478 | except: 479 | fig["layout"]["yaxis"]["range"]=None 480 | 481 | else: 482 | fig["layout"]["yaxis"]["range"]=None 483 | 484 | 485 | return [ 486 | dcc.Graph( 487 | # config=dict(showSendToCloud=True), 488 | figure=fig 489 | ) 490 | ] 491 | 492 | 493 | # uses a trigger to identify the callback and if the button is used then jsonifys the selected data 494 | @app.callback( 495 | Output("download-text-index", "data"), 496 | [ 497 | Input("btn_download2", "n_clicks"), 498 | Input("datatable-interactivity", "selected_rows"), 499 | ], 500 | ) 501 | def func2(n_clicks, selected_rows): 502 | trigger_id = dash.callback_context.triggered[0]["prop_id"].split(".")[0] 503 | 504 | global downloaded_xs_data 505 | 506 | if trigger_id == "btn_download2": 507 | if n_clicks is None: 508 | raise dash.exceptions.PreventUpdate 509 | else: 510 | if len(downloaded_xs_data) > 0: 511 | 512 | all_x_y_data = [] 513 | for k, v in downloaded_xs_data.items(): 514 | if k in selected_rows: 515 | all_x_y_data.append(downloaded_xs_data[k]) 516 | 517 | return dict( 518 | content=dumps(all_x_y_data, indent=2), 519 | filename="xsplot_download.json", 520 | ) 521 | 522 | 523 | if __name__ == "__main__": 524 | app.run_server(debug=True, host="0.0.0.0", port=8080) 525 | -------------------------------------------------------------------------------- /assets/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openmc-data-storage/isotope-xs-plotter/5f7044ca86c0c3a35f28ad7bbbe8c92709db659e/assets/android-chrome-192x192.png -------------------------------------------------------------------------------- /assets/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openmc-data-storage/isotope-xs-plotter/5f7044ca86c0c3a35f28ad7bbbe8c92709db659e/assets/android-chrome-512x512.png -------------------------------------------------------------------------------- /assets/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openmc-data-storage/isotope-xs-plotter/5f7044ca86c0c3a35f28ad7bbbe8c92709db659e/assets/apple-touch-icon.png -------------------------------------------------------------------------------- /assets/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openmc-data-storage/isotope-xs-plotter/5f7044ca86c0c3a35f28ad7bbbe8c92709db659e/assets/favicon-16x16.png -------------------------------------------------------------------------------- /assets/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openmc-data-storage/isotope-xs-plotter/5f7044ca86c0c3a35f28ad7bbbe8c92709db659e/assets/favicon-32x32.png -------------------------------------------------------------------------------- /assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openmc-data-storage/isotope-xs-plotter/5f7044ca86c0c3a35f28ad7bbbe8c92709db659e/assets/favicon.ico -------------------------------------------------------------------------------- /assets/site.webmanifest: -------------------------------------------------------------------------------- 1 | {"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} -------------------------------------------------------------------------------- /assets/typography.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Helvetica, sans-serif; 3 | } 4 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pandas 2 | plotly 3 | dash 4 | tables 5 | --------------------------------------------------------------------------------