Gay-Lussac's Law of Gases, P v T

Stephen Lukacs (2) iquanta.org/instruct/python
Enter Pressure vs. Temperature trial below...
(T(°C), P(kPa), Data Entry.. .


, or, just Upload to run the demonstration.
"""
reference: https://iquanta.org/instruct/python ::: Chemistry 9: P v T, Gay-Lussac's Law of Gases, P v T ::: Stephen Lukacs, Ph.D. ©2021-11-28
"""
from py4web import request
from yatl.helpers import *
from iquanta.mcp import is_str_float, str_to_float
from numpy import mean, std, polyfit
import plotly.graph_objects as go

BR, B = TAG['br/'], TAG['b']

demo_data = "0.4, 101.95\n0.1, 102.02\n0.1, 101.89\n0.0, 101.82\n-0.1, 101.82\n0.0, 101.76\n0.0, 101.82\n0.0, 101.89\n0.0, 101.82\n0.0, 101.82\n0.0, 101.82\n0.1, 101.82\n0.3, 101.82\n0.2, 101.89\n1.5, 102.02\n0.6, 102.34\n0.0, 102.53\n0.0, 102.73\n1.4, 102.86\n4.6, 102.86\n6.8, 102.99\n4.8, 103.05\n3.9, 103.24\n9.5, 103.31\n11.8, 103.57\n12.4, 103.37\n12.5, 103.63\n13.0, 103.76\n10.7, 103.83\n10.7, 104.08\n10.9, 104.6\n8.6, 105.37\n10.2, 105.7\n11.0, 106.02\n11.3, 106.02\n11.7, 106.21\n11.6, 106.28\n11.4, 106.54\n13.2, 106.67\n13.3, 106.86\n15.7, 107.05\n16.9, 107.38\n17.1, 107.18\n19.3, 107.44\n19.2, 107.96\n20.3, 108.15\n20.9, 107.76\n21.5, 107.96\n21.6, 108.99\n20.8, 109.83\n21.5, 110.22\n24.0, 110.47\n25.2, 111.05\n27.0, 111.57\n27.4, 111.89\n28.6, 112.54\n30.0, 112.93\n31.9, 113.44\n33.5, 114.02\n34.8, 114.48\n36.2, 115.06\n38.2, 115.83\n39.2, 116.22\n40.9, 116.8\n42.1, 117.25\n44.0, 117.77\n45.4, 118.09\n46.4, 118.74\n47.7, 119.13\n49.5, 119.58\n50.3, 120.03\n52.3, 120.48\n53.1, 120.8\n54.5, 121.26\n55.6, 121.64\n56.7, 121.9\n57.8, 122.35\n59.4, 122.74\n60.6, 123.13\n61.3, 123.64\n62.5, 124.03\n63.7, 124.42\n64.7, 124.74\n65.3, 125.0\n67.2, 125.39\n68.3, 125.71\n69.2, 126.03\n70.0, 126.35\n71.0, 126.61\n71.8, 127.0\n72.8, 127.39\n73.4, 127.58\n74.7, 127.84\n75.6, 128.23\n76.3, 128.55\n76.7, 128.81\n77.9, 129.13\n79.1, 129.39\n79.7, 129.65\n79.9, 129.97\n80.6, 130.1\n81.8, 130.29\n82.3, 130.61\n82.7, 130.87\n83.7, 131.07\n83.9, 131.26\n84.9, 131.52\n85.5, 131.78\n85.5, 132.03\n85.9, 132.16\n86.7, 132.29\n86.9, 132.49\n87.7, 132.62\n88.3, 132.81\n89.0, 133.0\n89.3, 133.2\n89.5, 133.26\n89.8, 133.52\n90.2, 133.58\n90.6, 133.72\n90.8, 133.85\n91.4, 134.04\n92.0, 134.16\n92.0, 134.36\n92.3, 134.56\n92.8, 134.62\n93.0, 134.75\n93.2, 134.87\n93.5, 134.94\n93.7, 135.14\n94.1, 135.27\n94.4, 135.27\n94.6, 135.46\n95.0, 135.52\n95.1, 135.65\n95.4, 135.78\n95.6, 135.85\n95.7, 135.98\n96.2, 136.1\n96.2, 136.17\n96.3, 136.17\n96.3, 136.3\n96.7, 136.43\n96.8, 136.56\n96.6, 136.69\n97.2, 136.69\n96.8, 136.75\n96.9, 136.75\n97.4, 136.81\n97.4, 136.88\n97.4, 137.01\n97.0, 137.07\n97.7, 137.2\n98.0, 137.2\n98.0, 137.33\n97.8, 137.4\n98.0, 137.46\n98.2, 137.46\n98.6, 137.59\n97.9, 137.65\n98.4, 137.65\n98.6, 137.65\n98.7, 137.72\n98.6, 137.72\n98.4, 137.85\n98.5, 137.72\n98.4, 137.85\n98.7, 137.85\n98.4, 137.85\n98.6, 137.91\n98.7, 137.98\n98.6, 137.91\n98.8, 137.98\n98.8, 137.98\n98.6, 138.04\n98.8, 138.04\n98.9, 138.04\n98.6, 138.11\n99.0, 138.17\n98.7, 138.17\n99.0, 138.23\n98.9, 138.3\n99.0, 138.36\n99.1, 138.36\n99.0, 138.43\n99.1, 138.43\n99.0, 138.43\n98.8, 138.36\n98.9, 138.3\n99.0, 138.36\n98.9, 138.3\n99.1, 138.23\n98.9, 138.23\n99.1, 138.17"

rtn = FORM(_action=None, _method='post')
if ('txtfile' in request.forms):
    txtfile = request.forms['txtfile']
    if (txtfile.count('\t') > 0):
        txtfile = '\n'.join([l.strip().replace('\t', ', ') for l in txtfile.replace('\r','').split('\n')])
        rtn.append(CAT("Converted.. .", PRE(r"{}".format(txtfile.encode('utf-8'))), BR()))
    txt = txtfile
    data = [ ]
    for l in txt.split('\n'):
        if (l.find(',') > -1) and is_str_float(l.split(',')[0]):
            data.append(tuple([str_to_float(d.strip()) for d in l.strip().split(',')]))
    #rtn.append(CAT(data, BR()))

rtn.append(STYLE("input[type=text] { width: 70px; text-align: center; border-radius: 7px; } textarea { margin: 0px; width: 295px; height: 200px; border-radius: 5px; } p { margin: 2px 0px; padding: 8px; border-radius: 10px; border: 2px solid silver; }"))
rtn.append(CAT(DIV("Enter Pressure vs. Temperature trial below...", BR(), "(T(°C), P(kPa), Data Entry.. .", BR(), TEXTAREA(txtfile if ('txtfile' in locals()) else demo_data, _name="txtfile"), _style="float:left;"), DIV(*[BR()]*2, INPUT(_type="submit", _value="Upload"), ", or, just Upload to run the demonstration.", _style="float:left; margin: 5px;"), DIV(_style="float:none;clear:both;")))

def doFit(x, y, deg):
    fit, ymean = polyfit(x, y, deg, full=True), mean(y)
    #RSquared verified with Mathematica LinearModelFit["RSquared"]
    SSres, SStot = fit[1][0], sum([(d - ymean)**2 for d in y])
    return fit[0], (1 - SSres / SStot)

if ('data' in locals()):
    x, y = [d[0]+273.15 for d in data], [d[1] for d in data]
    fit1st, RSquared1st = doFit(x, y, 1)
    fxn1st = lambda T: fit1st[0]*T + fit1st[1]
    x_intercept = abs(fit1st[1] / fit1st[0])
    rtn.append(SPAN("linear fit: ", 'P = {0:.4f}*T + {1:.4f}'.format(*fit1st), XML("&emsp;...&emsp;R<sup>2</sup> = "), "%.5f" % RSquared1st, BR(), f"with the |x-intercept| of {x_intercept:.3f}K nearly absolute zero indicates a good experiment.", _style="font-size:24pt;"))

    fig = go.Figure()
    fig.add_trace(go.Scatter(x=x, y=[fxn1st(d) for d in x], mode="lines", name="fit"))
    fig.add_trace(go.Scatter(x=x, y=y, mode='markers', name="data", marker=go.scatter.Marker(color="Black")))
    fig.update_layout(xaxis=go.XAxis(title="Temperature (K)"), yaxis=go.YAxis(title="Pressure (kPa)", anchor='x', side='left'))
    fig.update_layout(title_text="Gay-Lussac's Law of Gases, Pressure versus Temperature at Constant Volume", height=750)
    html = fig.to_html()
    rtn.append(XML(html[html.find('<div>'):html.rfind('</div>')+6].replace('<div>', '<div id="plotly">')))