Commit 7af43381 authored by Theresa Pollinger's avatar Theresa Pollinger
Browse files

automated simulation and visualization

parent 1798600b
This diff is collapsed.
import os
from pathlib import Path
import subprocess
from collections import OrderedDict
from pylatexenc.latexencode import utf8tolatex, utf82latex
......@@ -241,14 +242,23 @@ class ExaOutput:
"configList=\"\" \n"\
"configList+=\"{}/{} \" \n".format(self.probname, self.probname))
class ExaRunner():
class ExaRunner:
"""A class to run exastencils using the files generated by an Exaoutput class, and to get the results"""
from functools import lru_cache
def __init__(self, exaout):
self.exaout = exaout
def run_exastencils(self):
p = subprocess.run(["./generate_compile_and_run_list.sh"], cwd=str(self.exaout.exastencils_path),
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
out = p.stdout
if p.returncode != 0:
print(out)
@lru_cache()
def load_data(self, data_name="u"):
def load_data(self, data_name="u"): #TODO name of unknowns
import pandas as pd
data_path = self.exaout.exastencils_path.joinpath("generated").joinpath(self.exaout.probname).joinpath(data_name).with_suffix(".dat")
df = pd.read_csv(data_path, sep=' ', index_col=0)
......
from sys import executable
from os.path import join
#from pathlib import Path
from metakernel import MetaKernel
from IPython.display import HTML, Javascript
from metakernel import IPythonKernel
......@@ -20,6 +21,8 @@ from bokeh.models import ColumnDataSource
#from tempfile import NamedTemporaryFile
from pde_state_machine import *
from string_handling import build_url, get_recursively
# This "main class" is two things: a REPL loop, by subclassing the cmd2 Cmd class
# and a state machine as given by the pytransitions package
......@@ -50,7 +53,7 @@ class Interview(MetaKernel):
def __init__(self, **kwargs):
self.state_machine = PDE_States(self.poutput, self.update_prompt, self.please_prompt)
self.state_machine = PDE_States(self.poutput, self.update_prompt, self.please_prompt, self.display_html)
# call superclass constructor
super(Interview, self).__init__(**kwargs)
......@@ -143,14 +146,11 @@ class Interview(MetaKernel):
if arg.startswith("recap"):
self.state_machine.recap(arg)
return True
if arg.startswith("html"):
self.display_html()
if arg.startswith("tgview"):
self.display_tgview(arg)
return True
if arg.startswith("plt"):
self.display_plt()
return True
if arg.startswith("bokeh"):
self.display_bokeh()
if arg.startswith("undo"):
self.do_undo(arg)
return True
return False
......@@ -207,7 +207,9 @@ class Interview(MetaKernel):
super(Interview, self).do_complete(code, cursor_pos)
return
def display_html(self):
def display_html(self, code=None):
# highlight some of the code entered and show line numbers (just to play around)
self.Display(HTML("""
<style type="text/css">
.styled-background { background-color: #ff7; }
......@@ -226,85 +228,43 @@ class Interview(MetaKernel):
</script>
""" % (1, 0, 3, 0)))
tgview_url = join(self.state_machine.mmtinterface.serverInstance, "graphs/tgview.html?type=thgraph&graphdata=", self.state_machine.mmtinterface.URIprefix, self.state_machine.mmtinterface.namespace + "?u")
if code:
self.Display(HTML(code))
def display_tgview(self, args=''):
"""displays the theory graph viewer as html, cf. https://github.com/UniFormal/TGView/wiki/"""
server_url = str(self.state_machine.mmtinterface.serverInstance)
args_dict = {
"type": "thgraph",
}
args = args.replace("tgview ", '', 1).strip()
if args == '':
args_dict["graphdata"] = self.state_machine.mmtinterface.URIprefix + \
self.state_machine.mmtinterface.namespace + "?u"
else:
args_dict["graphdata"] = self.state_machine.mmtinterface.URIprefix + \
self.state_machine.mmtinterface.namespace + "?" + args
# if applicable, highlight the ephemeral parts https://github.com/UniFormal/TGView/issues/25
thynames = get_recursively(self.state_machine.simdata, "theoryname")
if thynames:
args_dict["highlight"] = ",".join(thynames)
othercode = """
tgview_url = build_url(server_url, "graphs/tgview.html", args_dict=args_dict)
code = """
<iframe
src="https://mmt.mathhub.info/graphs/tgview.html"
src="{}"
style="width: 100%; height: 510px; border: none"
>
</iframe>
"""
# a futile attempt to set the size
metadata = {
#'text/html': {
'width': 1600,
'height': 1400
#}
}
self.Display(HTML(othercode, metadata=metadata))
# cf. nbviewer.jupyter.org/github/bokeh/bokeh-notebooks/blob/master/tutorial/01 - Basic Plotting.ipynb
def display_bokeh(self):
from exaoutput import ExaOutput, ExaRunner
# create a new plot with default tools, using figure
p = figure(plot_width=1000, plot_height=400)
runner = ExaRunner(ExaOutput())
data = runner.load_data("u")
#source = ColumnDataSource(data=data)
source = ColumnDataSource(data=dict(x=[], u=[]))
source.data = source.from_df(data )#[['x', 'u']])
source.add(data.index, 'index')
# add a circle renderer with a size, color, and alpha
p.circle(x='index', y='u', size=2, line_color="navy", fill_color="orange", fill_alpha=0.5, source=source)
#show(p)
""".format(tgview_url)
self.display_html(code)
output_notebook()
# cf. http://bokeh.pydata.org/en/0.10.0/docs/user_guide/embed.html
self.Display(HTML(file_html(p, CDN, "my plot"))) # show the results
# using JS requires jupyter widgets extension
#script, div = components(p)
#div = notebook_div(p)
#self.Display(Javascript(script + div)) # show the results
def display_plt(self):
# plt.ion()
# plot = plt.plot([3, 8, 2, 5, 1])
#self.Display(plot)
# plt.show() #TODO find out why there is no comm and interactive shell - and if it should be there
# cf. http://ipython-books.github.io/16-creating-a-simple-kernel-for-jupyter/
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot([3, 8, 2, 5, 1])
# We create a PNG out of this plot.
png = _to_png(fig)
# and send it along as rich content
self.richcontent = dict()
# We prepare the response with our rich data (the plot).
self.richcontent['source'] = 'kernel'
# This dictionary may contain different MIME representations of the output.
self.richcontent['data'] = {
'image/png': png #TODO error: Notebook JSON is invalid: [{'image/png': ...
},
# We can specify the image size in the metadata field.
self.richcontent['metadata'] = {
'image/png': {
'width': 600,
'height': 400
}
}
self.poutput("image!")
if __name__ == '__main__':
# from ipykernel.kernelapp import IPKernelApp
......
......@@ -28,12 +28,8 @@ def start_mmt_server(port_number, mmtjar):
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
out = p.stdout
if p.returncode != 0 and p.returncode != 130:
raise MMTServerError("Could not start the MMT server, return code " + str(p.returncode) + ", " + str(out))
#outs, errs = p.communicate()
#if outs is not None:
#print("MMT server terminated before rest of program, ") # this just wont stay alive!!!
#p.terminate()
#raise MMTServerError("MMT server terminated before rest of program, ") # + p.stdout)
raise MMTServerError("Server aborted, return code " + str(p.returncode) + ", " + str(out))
def exit_mmt_server(port_number, mmtjar, timeout=3.0):
completed = subprocess.run(["/usr/bin/java", "-jar", mmtjar, ":send", str(port_number),
......@@ -41,9 +37,10 @@ def exit_mmt_server(port_number, mmtjar, timeout=3.0):
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
if completed.stdout != None and "(Connection refused)" in str(completed.stdout):
if timeout > 0.0:
start_mmt_extension(port_number, mmtjar, timeout - 0.1)
exit_mmt_server(port_number, mmtjar, timeout - 0.1)
else:
raise MMTServerError("unable to start interview extension")
raise MMTServerError("unable to exit mmt server")
def start_mmt_extension(port_number, mmtjar, timeout=3.0):
time.sleep(0.1) # hope for server to have started communication already
......
......@@ -7,9 +7,14 @@ from collections import OrderedDict
import getpass
from string_handling import *
from exaoutput import ExaOutput
from exaoutput import ExaOutput, ExaRunner
from mmtinterface import *
from bokeh.io import output_notebook, show, export_svgs
from bokeh.plotting import figure
from bokeh.resources import CDN
from bokeh.embed import file_html, components#, notebook_div
from bokeh.models import ColumnDataSource
class InterviewError(Exception):
"""Errors that occur during the course of the interview and are not due to mmt server errors"""
......@@ -61,12 +66,14 @@ class CriticalSubdict():
class PDE_States:
"""Just a state machine using pytranisitions that walks our theory graph and creates ephemeral theories and views"""
def __init__(self, output_function, after_state_change_function, prompt_function):
def __init__(self, output_function, after_state_change_function, prompt_function, display_html_function=None):
# just act like we were getting the right replies from MMT
self.cheating = True
# callback handles
self.poutput = output_function
self.please_prompt = prompt_function
self.display_html = display_html_function
# Initialize a state machine
self.states = [
......@@ -297,8 +304,10 @@ class PDE_States:
self.poutput("")
self.poutput("To get explanations, enter \"explain <optional keyword>\". ")
self.poutput("To see a recap of what we know so far, enter \"recap <optional keyword>\". ")
self.poutput("To interact with the current theory graph, enter \"tgwiev <optional theory name>\". ")
self.poutput("Otherwise, you can always try and use LaTeX-type input.")
self.poutput("Have a look at the currently loaded MMT theories under " + self.mmtinterface.serverInstance)
self.poutput("")
self.poutput("You can inspect the currently loaded MMT theories under " + self.mmtinterface.serverInstance)
self.poutput("")
self.poutput("")
self.poutput("")
......@@ -791,7 +800,8 @@ class PDE_States:
##### for state sim
def sim_begin(self):
self.please_prompt("Would you like to try and solve the PDE using the Finite Difference Method in ExaStencils?",
self.please_prompt("Would you like to try and solve the PDE using the Finite Difference Method in ExaStencils?"
"If yes, you can provide a configuration name, or we'll just use your name.",
self.sim_ok_fd)
def sim_handle_input(self, userstring):
......@@ -801,9 +811,42 @@ class PDE_States:
def sim_exit(self):
# generate output
ExaOutput(self.simdata)
self.poutput("Generated ExaStencils input.")
# TODO generate and run simulation
exaout = ExaOutput(self.simdata)
print("Generated ExaStencils input; running ExaStencils")
# generate and run simulation
runner = ExaRunner(exaout)
runner.run_exastencils()
print("Ran ExaStencils; preparing visualization")
# output
self.display_result_as_bokeh()
# cf. nbviewer.jupyter.org/github/bokeh/bokeh-notebooks/blob/master/tutorial/01 - Basic Plotting.ipynb
def display_result_as_bokeh(self):
# create a new plot with default tools, using figure
p = figure(plot_width=1000, plot_height=400)
runner = ExaRunner(ExaOutput())
data = runner.load_data("u")
#source = ColumnDataSource(data=data)
source = ColumnDataSource(data=dict(x=[], u=[]))
source.data = source.from_df(data)
source.add(data.index, 'index')
# add a circle renderer with a size, color, and alpha
p.circle(x='index', y='u', size=2, line_color="navy", fill_color="orange", fill_alpha=0.5, source=source)
#show(p)
output_notebook()
# cf. http://bokeh.pydata.org/en/0.10.0/docs/user_guide/embed.html
self.display_html(file_html(p, CDN, "my plot")) # show the results
# using JS requires jupyter widgets extension
#script, div = components(p)
#div = notebook_div(p)
#self.Display(Javascript(script + div)) # show the results
def sim_ok_fd(self):
......
......@@ -2,6 +2,7 @@
from distutils.util import strtobool
import re
from urllib.parse import urlparse, urlencode, ParseResult
def means_no(answer):
try:
......@@ -12,6 +13,14 @@ def means_no(answer):
return False
return False
def build_url(baseurl, path, args_dict={}, query_dict={}):
# Returns a list in the structure of urlparse.ParseResult
url = urlparse(baseurl)
# construct new parseresult
new_url = ParseResult(url.scheme, url.netloc, path, urlencode(args_dict),
urlencode(query_dict, doseq=True), url.fragment)
return new_url.geturl()
# cf. https://stackoverflow.com/questions/14962485/finding-a-key-recursively-in-a-dictionary
def get_recursively(search_dict, field):
"""
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment