File size: 7,159 Bytes
b11aca7
4eb270e
 
 
b11aca7
a29f92f
4eb270e
b11aca7
 
4eb270e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b11aca7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4eb270e
b11aca7
 
4eb270e
b11aca7
4eb270e
b11aca7
4eb270e
b11aca7
4eb270e
b11aca7
 
4eb270e
 
 
 
 
b11aca7
4eb270e
b11aca7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4eb270e
 
b11aca7
 
 
 
 
 
 
 
4eb270e
 
b11aca7
 
10c94eb
b11aca7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10c94eb
b11aca7
 
 
 
 
 
 
 
 
 
 
 
4eb270e
b11aca7
 
 
 
 
 
4eb270e
 
 
b11aca7
 
4eb270e
b11aca7
 
 
4eb270e
 
 
 
 
 
 
 
 
b11aca7
 
10c94eb
3db7729
b11aca7
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
import streamlit as st, pandas as pd, os, io
from modeci_mdf.mdf import Model, Graph, Node, Parameter, OutputPort
from modeci_mdf.utils import load_mdf_json, load_mdf, load_mdf_yaml
from modeci_mdf.execution_engine import EvaluableGraph
import json
st.set_page_config(layout="wide")
st.title("🔋 MDF Simulator")
import requests
# models: Purpose: To store the state of the model and update the model
def run_simulation(param_inputs, mdf_model):
    mod_graph = mdf_model.graphs[0]
    nodes = mod_graph.nodes[0]
    parameters = nodes.parameters
    outputs = nodes.output_ports
    eg = EvaluableGraph(mod_graph, verbose=False)
    duration = param_inputs["Simulation Duration (s)"]
    dt = param_inputs["Time Step (s)"]
    t = 0
    times = []
    output_values = {op.value: [] for op in outputs}
    while t <= duration:
        times.append(t)
        if t == 0:
            eg.evaluate()
        else:
            eg.evaluate(time_increment=dt)

        for param in output_values:
            eval_param = eg.enodes[nodes.id].evaluable_parameters[param]
            output_values[param].append(eval_param.curr_value)
        t += dt
    chart_data = pd.DataFrame(output_values)
    chart_data['Time'] = times
    chart_data.set_index('Time', inplace=True)
    return chart_data

# views: Purpose: To display the state of the model and update the view
def show_simulation_results(chart_data):
    st.line_chart(chart_data, use_container_width=True, height=400)
    st.write("Output Values")
    st.write(chart_data)

def show_mdf_graph(mdf_model):
    st.subheader("MDF Graph")
    mdf_model.to_graph_image(engine="dot", output_format="png", view_on_render=False, level=3, filename_root=mdf_model.id, only_warn_on_fail=(os.name == "nt"))
    image_path = mdf_model.id + ".png"
    st.image(image_path, caption="Model Graph Visualization")

def show_json_output(mdf_model):
    st.subheader("JSON Output")
    st.json(mdf_model.to_json())

def view_tabs(mdf_model, param_inputs): # view
    tab1, tab2, tab3 = st.tabs(["Simulation Results", "MDF Graph", "Json Output"])
    with tab1:
        chart_data = run_simulation(param_inputs, mdf_model) # model
        show_simulation_results(chart_data) # view
    with tab2:
        show_mdf_graph(mdf_model) # view
    with tab3:
        show_json_output(mdf_model) # view

def parameter_form_to_update_model_and_view(mdf_model, parameters, param_inputs, mod_graph, nodes):
    form = st.form(key="parameter_form")
    valid_inputs = True

    for i, param in enumerate(parameters):
        if isinstance(param.value, str) or param.value is None:
            continue  
        key = f"{param.id}_{i}"
        if mdf_model.metadata:
            value = form.text_input(f"{param.metadata.get('description', param.id)} ({param.id})", value=str(param.value), key=key)
        else:
            value = form.text_input(f"{param.id}", value=str(param.value), key=key)
        
        try:
            param_inputs[param.id] = float(value)
        except ValueError:
            st.error(f"Invalid input for {param.id}. Please enter a valid number.")
            valid_inputs = False

    sim_duration = form.text_input("Simulation Duration (s)", value=str(param_inputs["Simulation Duration (s)"]), key="sim_duration")
    time_step = form.text_input("Time Step (s)", value=str(param_inputs["Time Step (s)"]), key="time_step")

    try:
        param_inputs["Simulation Duration (s)"] = float(sim_duration)
    except ValueError:
        st.error("Invalid input for Simulation Duration. Please enter a valid number.")
        valid_inputs = False

    try:
        param_inputs["Time Step (s)"] = float(time_step)
    except ValueError:
        st.error("Invalid input for Time Step. Please enter a valid number.")
        valid_inputs = False

    run_button = form.form_submit_button("Run Simulation")
    if run_button:
        if valid_inputs:
            for param in parameters:
                if param.id in param_inputs:
                    param.value = param_inputs[param.id]
            view_tabs(mdf_model, param_inputs)
        # else:
        #     st.error("Please correct the invalid inputs before running the simulation.")


def upload_file_and_load_to_model():
    st.write("Choose how to load the model:")
    load_option = st.radio("", ("Upload File", "GitHub URL"))

    if load_option == "Upload File":
        uploaded_file = st.file_uploader("Choose a JSON/YAML/BSON file", type=["json", "yaml", "bson"])
        if uploaded_file is not None:
            file_content = uploaded_file.getvalue()
            file_extension = uploaded_file.name.split('.')[-1].lower()
            return load_model_from_content(file_content, file_extension)
    else:
        st.write("sample_github_url = https://raw.githubusercontent.com/ModECI/MDF/development/examples/MDF/NewtonCoolingModel.json")
        github_url = st.text_input("Enter GitHub raw file URL:", placeholder="Enter GitHub raw file URL")
        if github_url:
            try:
                response = requests.get(github_url)
                response.raise_for_status()
                file_content = response.content
            # print(file_content)
                file_extension = github_url.split('.')[-1].lower()
                return load_model_from_content(file_content, file_extension)
            except requests.RequestException as e:
                st.error(f"Error loading file from GitHub: {e}")
                return None

    return None

def load_model_from_content(file_content, file_extension):
    try:
        if file_extension == 'json':
            json_data = json.loads(file_content)
            mdf_model = Model.from_dict(json_data)
        elif file_extension in ['yaml', 'yml']:
            mdf_model = load_mdf_yaml(io.BytesIO(file_content))
        else:
            st.error("Unsupported file format. Please use JSON or YAML files.")
            return None
        
        st.session_state.original_mdf_model = mdf_model  # Save the original model
        st.session_state.mdf_model_yaml = mdf_model  # Save the current model state
        return mdf_model
    except Exception as e:
        st.error(f"Error loading model: {e}")
        return None


def main():
    st.write("Text box changed to input. Github URL is allowed. Added some warnings eg. on adding text in input fields. Now working on multiple parameters allow.")
    mdf_model = upload_file_and_load_to_model() # controller
    if mdf_model:
        mod_graph = mdf_model.graphs[0]
        nodes = mod_graph.nodes[0]
        parameters = nodes.parameters
        param_inputs = {}
        if mdf_model.metadata:
            preferred_duration = float(mdf_model.metadata.get("preferred_duration", 10))
            preferred_dt = float(mdf_model.metadata.get("preferred_dt", 0.1))
        else:
            preferred_duration = 100
            preferred_dt = 0.1
        param_inputs["Simulation Duration (s)"] = preferred_duration
        param_inputs["Time Step (s)"] = preferred_dt
        parameter_form_to_update_model_and_view(mdf_model, parameters, param_inputs, mod_graph, nodes)

if __name__ == "__main__":
    main()