Skip to content

Commit fe6c83b

Browse files
authored
Re-write H2SCalculator for accuracy (#29)
- H2SCalculator is now a backend service, which is more accurate and reliable. - The frontend component now fetches the calculation from the backend service rather than doing client side calculations, which were inaccurate before (didn't match original H2SCalculator). - The frontend component now correctly tracks download data. Before it only tracked the download data from the most recent simulation. Now it tracks the downlaod data from all simulations that have been run.
1 parent d6c6d3d commit fe6c83b

11 files changed

Lines changed: 1812 additions & 1123 deletions

File tree

backend/app/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from app.routes.phreeqc import phreeqc_calc
55
from app.routes.supcrtbl import supcrtbl_calc
66
from app.routes import minerals
7+
from app.routes.h2s import h2s_calc
78

89
from fastapi import FastAPI, HTTPException, Request
910
from fastapi.responses import FileResponse, JSONResponse
@@ -39,6 +40,7 @@ async def custom_http_exception_handler(request: Request, exc: HTTPException):
3940
app.include_router(phreeqc_calc.router, tags=["phreeqc"])
4041
app.include_router(supcrtbl_calc.router, tags=["Supcrtbl"])
4142
app.include_router(co2_calc.router, tags=["CO2"])
43+
app.include_router(h2s_calc.router, tags=["H2S"])
4244
app.include_router(auth.router, tags=["auth"])
4345
app.include_router(user.router, tags=["user"])
4446
app.include_router(rate_calc.router, tags=["Rate"])

backend/app/routes/h2s/h2s_calc.py

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
2+
'''
3+
- resources is all the .csv data. Then h2s_request and h2s.py are the originals
4+
Just refactor so that it returns a matrix of data.
5+
'''
6+
7+
import os
8+
from fastapi import APIRouter, HTTPException
9+
import numpy as np
10+
from scipy.interpolate import griddata
11+
from pandas import read_csv as pd_read_csv
12+
router = APIRouter()
13+
14+
# Define PATHS to blocks here
15+
resource_dir = os.path.join(os.path.dirname(__file__), "resources")
16+
block1_path = os.path.join(resource_dir, "Block1.csv")
17+
block2_path = os.path.join(resource_dir, "Block2.csv")
18+
block3_path = os.path.join(resource_dir, "Block3.csv")
19+
20+
@router.get("/api/h2s")
21+
def calculate_h2s(system: float, temp: float, mNaCl: float):
22+
# Reject when system is out of range.
23+
if system < 0 or system > 2:
24+
raise HTTPException(status_code=400, detail=f"System '{system}' is out of range. Select system from 0 to 2.")
25+
26+
# Correct boundaries
27+
if temp < 298.15:
28+
temp = 298.15
29+
elif system < 2 and temp > 373.15:
30+
temp = 373.15
31+
elif system == 2 and temp > 348.15:
32+
temp = 348.15
33+
if mNaCl < 0:
34+
mNaCl = 0
35+
elif system < 2 and mNaCl > 6:
36+
mNaCl = 6
37+
elif system == 2 and mNaCl > 4:
38+
mNaCl = 4
39+
40+
results = computeBlock(system, temp, mNaCl)
41+
return results
42+
43+
44+
def extractSampleData_xyz(filename, block):
45+
df = pd_read_csv(filename)
46+
x = np.array(df["T"])
47+
y = np.array(df["P"])
48+
z = np.array(df["NaCl"])
49+
50+
if block in [1,2]:
51+
v1 = np.array(df["xH2S"])
52+
v2 = np.array(df["r"])
53+
v3 = np.array(df["H2S"])
54+
return (x,y,z,(v1,v2,v3))
55+
else:
56+
v = np.array(df["xH2S+xCO2"])
57+
return (x,y,z,v)
58+
59+
def createInputs(temp,nacl):
60+
X = np.ones(61) * temp
61+
Y = np.arange(0,610,step=10)
62+
Z = np.ones(61) * nacl
63+
return (X,Y,Z)
64+
65+
def block1(temp, nacl):
66+
(x,y,z,(v1,v2,v3)) = extractSampleData_xyz(block1_path,1)
67+
(X,Y,Z) = createInputs(temp,nacl)
68+
F1 = griddata((x,y,z),v1,(X,Y,Z),method='linear')
69+
F2 = griddata((x,y,z),v2,(X,Y,Z),method='linear')
70+
F3 = griddata((x,y,z),v3,(X,Y,Z),method='linear')
71+
72+
# NOTE: H2S calculator is supposed to return a matrix of floating point
73+
# numbers for the client. However, due to the nature of the calculations, sometimes
74+
# some of the elements in the matrix will be nan, which cannot be put inside JSON.
75+
# So we'll turn all the elements in the matrix into strings first, and then return the matrix.
76+
# On the client side, we'll parse the strings back into floats, and things will work out.
77+
return [
78+
list(map(str, F1.tolist())),
79+
list(map(str, F2.tolist())),
80+
list(map(str, F3.tolist()))
81+
]
82+
83+
def block2(temp, nacl):
84+
(x,y,z,(v1,v2,v3)) = extractSampleData_xyz(block2_path,2)
85+
(X,Y,Z) = createInputs(temp,nacl)
86+
F1 = griddata((x,y,z),v1,(X,Y,Z),method='linear')
87+
F2 = griddata((x,y,z),v2,(X,Y,Z),method='linear')
88+
F3 = griddata((x,y,z),v3,(X,Y,Z),method='linear')
89+
return [
90+
list(map(str ,F1.tolist())),
91+
list(map(str ,F2.tolist())),
92+
list(map(str ,F3.tolist()))
93+
]
94+
95+
def block3(temp, nacl):
96+
(x,y,z,v) = extractSampleData_xyz(block3_path,3)
97+
(X,Y,Z) = createInputs(temp,nacl)
98+
F = griddata((x,y,z),v,(X,Y,Z),method='linear')
99+
return [
100+
list(map(str, F.tolist()))
101+
]
102+
103+
def computeBlock(blockNumber, temp, nacl):
104+
if blockNumber == 0:
105+
return block1(temp, nacl)
106+
elif blockNumber == 1:
107+
return block2(temp, nacl)
108+
else:
109+
return block3(temp, nacl)
110+

0 commit comments

Comments
 (0)