mirror of
https://github.com/ANL-CEEESA/LLEPE.git
synced 2025-12-06 01:48:53 -06:00
Completed one composition parameter estimation
This commit is contained in:
5
.idea/dictionaries/Titus.xml
generated
5
.idea/dictionaries/Titus.xml
generated
@@ -1,12 +1,17 @@
|
||||
<component name="ProjectDictionaryState">
|
||||
<dictionary name="Titus">
|
||||
<words>
|
||||
<w>coeffs</w>
|
||||
<w>diluant</w>
|
||||
<w>dodecane</w>
|
||||
<w>equilibrate</w>
|
||||
<w>extractant</w>
|
||||
<w>kmol</w>
|
||||
<w>ndarray</w>
|
||||
<w>pred</w>
|
||||
<w>reeps</w>
|
||||
<w>scipy</w>
|
||||
<w>thermo</w>
|
||||
</words>
|
||||
</dictionary>
|
||||
</component>
|
||||
53
.idea/workspace.xml
generated
53
.idea/workspace.xml
generated
@@ -1,14 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="f4439dc0-6756-4612-8f7d-596d8949f300" name="Default Changelist" comment="started project">
|
||||
<change afterPath="$PROJECT_DIR$/tests/__init__.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/tests/test_reeps.py" afterDir="false" />
|
||||
<list default="true" id="f4439dc0-6756-4612-8f7d-596d8949f300" name="Default Changelist" comment="">
|
||||
<change beforePath="$PROJECT_DIR$/.idea/dictionaries/Titus.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/dictionaries/Titus.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/data/xmls/elementz.xml" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/data/xmls/twophase.xml" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/reeps.py" beforeDir="false" afterPath="$PROJECT_DIR$/reeps.py" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/tests/test1.py" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/tests/test_reeps.py" beforeDir="false" afterPath="$PROJECT_DIR$/tests/test_reeps.py" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
@@ -43,6 +40,9 @@
|
||||
<module name="parameter-estimation" />
|
||||
<option name="INTERPRETER_OPTIONS" value="" />
|
||||
<option name="PARENT_ENVS" value="true" />
|
||||
<envs>
|
||||
<env name="PYTHONUNBUFFERED" value="1" />
|
||||
</envs>
|
||||
<option name="SDK_HOME" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/../../parameter estimation/parameter estimation" />
|
||||
<option name="IS_MODULE_SDK" value="false" />
|
||||
@@ -57,21 +57,18 @@
|
||||
<option name="INPUT_FILE" value="" />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
<configuration name="methods" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
|
||||
<configuration name="test" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
|
||||
<module name="parameter-estimation" />
|
||||
<option name="INTERPRETER_OPTIONS" value="" />
|
||||
<option name="PARENT_ENVS" value="true" />
|
||||
<envs>
|
||||
<env name="PYTHONUNBUFFERED" value="1" />
|
||||
</envs>
|
||||
<option name="SDK_HOME" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
||||
<option name="IS_MODULE_SDK" value="true" />
|
||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/../../parameter estimation/parameter estimation" />
|
||||
<option name="IS_MODULE_SDK" value="false" />
|
||||
<option name="ADD_CONTENT_ROOTS" value="true" />
|
||||
<option name="ADD_SOURCE_ROOTS" value="true" />
|
||||
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/methods.py" />
|
||||
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/../../parameter estimation/parameter estimation/test.py" />
|
||||
<option name="PARAMETERS" value="" />
|
||||
<option name="SHOW_COMMAND_LINE" value="false" />
|
||||
<option name="SHOW_COMMAND_LINE" value="true" />
|
||||
<option name="EMULATE_TERMINAL" value="false" />
|
||||
<option name="MODULE_MODE" value="false" />
|
||||
<option name="REDIRECT_INPUT" value="false" />
|
||||
@@ -134,7 +131,7 @@
|
||||
<recent_temporary>
|
||||
<list>
|
||||
<item itemvalue="Python.test_reeps" />
|
||||
<item itemvalue="Python.fit" />
|
||||
<item itemvalue="Python.test_reeps" />
|
||||
<item itemvalue="Python.test_reeps" />
|
||||
<item itemvalue="Python.test_reeps" />
|
||||
<item itemvalue="Python.test_reeps" />
|
||||
@@ -195,7 +192,14 @@
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1591047660677</updated>
|
||||
</task>
|
||||
<option name="localTasksCounter" value="5" />
|
||||
<task id="LOCAL-00005" summary="started project">
|
||||
<created>1591057566877</created>
|
||||
<option name="number" value="00005" />
|
||||
<option name="presentableId" value="LOCAL-00005" />
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1591057566877</updated>
|
||||
</task>
|
||||
<option name="localTasksCounter" value="6" />
|
||||
<servers />
|
||||
</component>
|
||||
<component name="Vcs.Log.Tabs.Properties">
|
||||
@@ -232,10 +236,10 @@
|
||||
<screen x="-1920" y="2" width="1920" height="1040" />
|
||||
</state>
|
||||
<state x="-1213" y="379" key="ANALYSIS_DLG_com.intellij.analysis.BaseAnalysisAction$1/0.0.1536.824/-1920.2.1920.1040@-1920.2.1920.1040" timestamp="1590787657711" />
|
||||
<state x="-1364" y="117" key="CommitChangelistDialog2" timestamp="1591048453208">
|
||||
<state x="-1364" y="117" key="CommitChangelistDialog2" timestamp="1591057565266">
|
||||
<screen x="-1920" y="2" width="1920" height="1040" />
|
||||
</state>
|
||||
<state x="-1364" y="117" key="CommitChangelistDialog2/0.0.1536.824/-1920.2.1920.1040@-1920.2.1920.1040" timestamp="1591048453208" />
|
||||
<state x="-1364" y="117" key="CommitChangelistDialog2/0.0.1536.824/-1920.2.1920.1040@-1920.2.1920.1040" timestamp="1591057565266" />
|
||||
<state x="-1828" y="94" width="1736" height="856" key="DiffContextDialog" timestamp="1591048879404">
|
||||
<screen x="-1920" y="2" width="1920" height="1040" />
|
||||
</state>
|
||||
@@ -276,14 +280,23 @@
|
||||
<screen x="-1920" y="2" width="1920" height="1040" />
|
||||
</state>
|
||||
<state x="-1368" y="256" key="Vcs.Push.Dialog.v2/0.0.1536.824/-1920.2.1920.1040@-1920.2.1920.1040" timestamp="1591047662716" />
|
||||
<state x="579" y="298" key="com.intellij.ide.util.TipDialog" timestamp="1591037480473">
|
||||
<screen x="0" y="0" width="1920" height="1040" />
|
||||
<state x="-1341" y="300" key="com.intellij.ide.util.TipDialog" timestamp="1591116828596">
|
||||
<screen x="-1920" y="2" width="1920" height="1040" />
|
||||
</state>
|
||||
<state x="-1341" y="300" key="com.intellij.ide.util.TipDialog/0.0.1536.824/-1920.2.1920.1040@-1920.2.1920.1040" timestamp="1591116828596" />
|
||||
<state x="463" y="236" key="com.intellij.ide.util.TipDialog/0.0.1536.824/-1920.2.1920.1040@0.0.1536.824" timestamp="1590784072879" />
|
||||
<state x="579" y="298" key="com.intellij.ide.util.TipDialog/0.0.1920.1040@0.0.1920.1040" timestamp="1591037480473" />
|
||||
<state x="769" y="438" key="com.intellij.openapi.vcs.update.UpdateOrStatusOptionsDialogupdate-v2" timestamp="1591037545358">
|
||||
<screen x="0" y="0" width="1920" height="1040" />
|
||||
</state>
|
||||
<state x="769" y="438" key="com.intellij.openapi.vcs.update.UpdateOrStatusOptionsDialogupdate-v2/0.0.1920.1040@0.0.1920.1040" timestamp="1591037545358" />
|
||||
<state x="-1219" y="166" key="refactoring.ChangeSignatureDialog" timestamp="1591059193684">
|
||||
<screen x="-1920" y="2" width="1920" height="1040" />
|
||||
</state>
|
||||
<state x="-1219" y="166" key="refactoring.ChangeSignatureDialog/0.0.1536.824/-1920.2.1920.1040@-1920.2.1920.1040" timestamp="1591059193684" />
|
||||
<state x="-1297" y="227" width="672" height="678" key="search.everywhere.popup" timestamp="1591113024379">
|
||||
<screen x="-1920" y="2" width="1920" height="1040" />
|
||||
</state>
|
||||
<state x="-1297" y="227" width="672" height="678" key="search.everywhere.popup/0.0.1536.824/-1920.2.1920.1040@-1920.2.1920.1040" timestamp="1591113024379" />
|
||||
</component>
|
||||
</project>
|
||||
165
reeps.py
165
reeps.py
@@ -1,5 +1,13 @@
|
||||
import time
|
||||
import cantera as ct
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
from scipy.optimize import minimize
|
||||
# noinspection PyPep8Naming
|
||||
import xml.etree.ElementTree as ET
|
||||
import seaborn as sns
|
||||
import matplotlib.pyplot as plt
|
||||
sns.set()
|
||||
|
||||
|
||||
class REEPS:
|
||||
@@ -7,13 +15,15 @@ class REEPS:
|
||||
Takes in experimental data
|
||||
Returns parameters for GEM
|
||||
:param exp_csv_filename: (str) csv file name with experimental data
|
||||
:param param_xml_file: (str) xml file with parameters for equilibrium calc
|
||||
:param phases_xml_filename: (str) xml file with parameters for equilibrium calc
|
||||
:param x_guess: (float) guess for multiplier variable
|
||||
:param h_guess: (float) initial guess standard enthalpy (J/kmol)
|
||||
:param phase_names: (list) names of phases in xml file
|
||||
:param aq_solvent_name: (str) name of aqueous solvent in xml file
|
||||
:param extractant_name: (str) name of extractant
|
||||
:param diluant_name: (str) name of diluant
|
||||
:param extractant_name: (str) name of extractant in xml file
|
||||
:param diluant_name: (str) name of diluant in xml file
|
||||
:param complex_name: (str) name of complex in xml file
|
||||
:param rare_earth_ion_name: (str) name of rare earth ion in xml file
|
||||
:param aq_solvent_rho: (float) density of solvent (g/L)
|
||||
:param extractant_rho: (float) density of extractant (g/L)
|
||||
:param diluant_rho: (float) density of extractant (g/L)
|
||||
@@ -22,32 +32,42 @@ class REEPS:
|
||||
|
||||
def __init__(self,
|
||||
exp_csv_filename,
|
||||
param_xml_file,
|
||||
phases_xml_filename,
|
||||
x_guess,
|
||||
h_guess,
|
||||
phase_names,
|
||||
aq_solvent_name,
|
||||
extractant_name,
|
||||
diluant_name,
|
||||
complex_name,
|
||||
rare_earth_ion_name,
|
||||
aq_solvent_rho=None,
|
||||
extractant_rho=None,
|
||||
diluant_rho=None,
|
||||
):
|
||||
self._exp_csv_filename = exp_csv_filename
|
||||
self._phases_xml_filename = phases_xml_filename
|
||||
self._x_guess = x_guess
|
||||
self._h_guess = h_guess
|
||||
self._phase_names = phase_names
|
||||
self._aq_solvent_name = aq_solvent_name
|
||||
self._extractant_name = extractant_name
|
||||
self._diluant_name = diluant_name
|
||||
self._complex_name = complex_name
|
||||
self._rare_earth_ion_name = rare_earth_ion_name
|
||||
self._aq_solvent_rho = aq_solvent_rho
|
||||
self._extractant_rho = extractant_rho
|
||||
self._diluant_rho = diluant_rho
|
||||
|
||||
self._phases = ct.import_phases(param_xml_file, phase_names)
|
||||
self._phases = ct.import_phases(phases_xml_filename, phase_names)
|
||||
self._exp_df = pd.read_csv(self._exp_csv_filename)
|
||||
|
||||
self._in_moles = None
|
||||
|
||||
self.set_in_moles()
|
||||
self._aq_ind = None
|
||||
self._org_ind = None
|
||||
|
||||
self.set_in_moles(feed_vol=1)
|
||||
|
||||
def get_exp_csv_filename(self) -> str:
|
||||
return self._exp_csv_filename
|
||||
@@ -60,8 +80,13 @@ class REEPS:
|
||||
def get_phases(self) -> list:
|
||||
return self._phases
|
||||
|
||||
def set_phases(self, param_xml_file, phase_names):
|
||||
self._phases = ct.import_phases(param_xml_file, phase_names)
|
||||
def set_phases(self, phases_xml_filename, phase_names):
|
||||
"""Change xml and phase names
|
||||
Also runs set_in_mole to set initial moles to 1 g/L"""
|
||||
self._phases_xml_filename = phases_xml_filename
|
||||
self._phase_names = phase_names
|
||||
self._phases = ct.import_phases(phases_xml_filename, phase_names)
|
||||
self.set_in_moles(feed_vol=1)
|
||||
return None
|
||||
|
||||
def get_x_guess(self) -> float:
|
||||
@@ -99,6 +124,20 @@ class REEPS:
|
||||
self._diluant_name = diluant_name
|
||||
return None
|
||||
|
||||
def get_complex_name(self) -> str:
|
||||
return self._complex_name
|
||||
|
||||
def set_complex_name(self, complex_name):
|
||||
self._complex_name = complex_name
|
||||
return None
|
||||
|
||||
def get_rare_earth_ion_name(self) -> str:
|
||||
return self._rare_earth_ion_name
|
||||
|
||||
def set_rare_earth_ion_name(self, rare_earth_ion_name):
|
||||
self._rare_earth_ion_name = rare_earth_ion_name
|
||||
return None
|
||||
|
||||
def get_aq_solvent_rho(self) -> str:
|
||||
return self._aq_solvent_rho
|
||||
|
||||
@@ -120,7 +159,8 @@ class REEPS:
|
||||
self._diluant_rho = diluant_rho
|
||||
return None
|
||||
|
||||
def set_in_moles(self):
|
||||
def set_in_moles(self, feed_vol):
|
||||
""":param feed_vol: (float) feed volume of mixture (g/L)"""
|
||||
phases_copy = self._phases.copy()
|
||||
exp_df = self._exp_df.copy()
|
||||
solvent_name = self._aq_solvent_name
|
||||
@@ -145,6 +185,8 @@ class REEPS:
|
||||
org_ind = 1
|
||||
else:
|
||||
org_ind = 0
|
||||
self._aq_ind = aq_ind
|
||||
self._org_ind = org_ind
|
||||
extractant_ind = phases_copy[org_ind].species_names.index(
|
||||
extractant_name)
|
||||
diluant_ind = phases_copy[org_ind].species_names.index(diluant_name)
|
||||
@@ -156,19 +198,18 @@ class REEPS:
|
||||
diluant_mw = mix_org.molecular_weights[diluant_ind]
|
||||
if solvent_rho is None:
|
||||
solvent_rho = mix_aq(aq_ind).partial_molar_volumes[
|
||||
solvent_ind]/solvent_mw * 1e6 # g/L
|
||||
solvent_ind] / solvent_mw * 1e6 # g/L
|
||||
self._aq_solvent_rho = solvent_rho
|
||||
if extractant_rho is None:
|
||||
extractant_rho = mix_org(org_ind).partial_molar_volumes[
|
||||
extractant_ind]/extractant_mw * 1e6
|
||||
extractant_ind] / extractant_mw * 1e6
|
||||
self._extractant_rho = extractant_rho
|
||||
if diluant_rho is None:
|
||||
diluant_rho = mix_org(org_ind).partial_molar_volumes[
|
||||
extractant_ind] / extractant_mw * 1e6
|
||||
extractant_ind] / extractant_mw * 1e6
|
||||
self._diluant_rho = diluant_rho
|
||||
|
||||
in_moles_data = []
|
||||
feed_vol = 1. # g/L
|
||||
aq_phase_solvent_moles = feed_vol * solvent_rho / solvent_mw
|
||||
for row in exp_df.values:
|
||||
h_plus_moles = feed_vol * row[0]
|
||||
@@ -176,7 +217,7 @@ class REEPS:
|
||||
rare_earth_moles = feed_vol * row[6]
|
||||
chlorine_moles = 3 * rare_earth_moles + h_plus_moles
|
||||
extractant_moles = feed_vol * row[3]
|
||||
extractant_vol = extractant_moles * extractant_rho / extractant_mw
|
||||
extractant_vol = extractant_moles * extractant_mw / extractant_rho
|
||||
diluant_vol = feed_vol - extractant_vol
|
||||
diluant_moles = diluant_vol * diluant_rho / diluant_mw
|
||||
complex_moles = 0
|
||||
@@ -196,3 +237,99 @@ class REEPS:
|
||||
|
||||
def get_in_moles(self) -> pd.DataFrame:
|
||||
return self._in_moles
|
||||
|
||||
def objective(self, x):
|
||||
h = x[0] * self._h_guess
|
||||
phases_copy = self._phases.copy()
|
||||
aq_ind = self._aq_ind
|
||||
org_ind = self._org_ind
|
||||
complex_name = self._complex_name
|
||||
rare_earth_ion_name = self._rare_earth_ion_name
|
||||
in_moles = self._in_moles
|
||||
exp_df = self._exp_df
|
||||
|
||||
complex_index = phases_copy[org_ind].species_index(complex_name)
|
||||
complex_species = phases_copy[org_ind].species(complex_index)
|
||||
|
||||
new_thermo_coeffs = complex_species.thermo.coeffs
|
||||
new_thermo_coeffs[1] = h
|
||||
complex_species.thermo = ct.ConstantCp(
|
||||
complex_species.thermo.min_temp,
|
||||
complex_species.thermo.max_temp,
|
||||
complex_species.thermo.reference_pressure,
|
||||
new_thermo_coeffs)
|
||||
phases_copy[org_ind].modify_species(complex_index, complex_species)
|
||||
|
||||
mix = ct.Mixture(phases_copy)
|
||||
pred = []
|
||||
for row in in_moles.values:
|
||||
mix.species_moles = row
|
||||
mix.equilibrate('TP', log_level=0)
|
||||
re_org = mix.species_moles[mix.species_index(
|
||||
org_ind, complex_name)]
|
||||
re_aq = mix.species_moles[mix.species_index(
|
||||
aq_ind, rare_earth_ion_name)]
|
||||
pred.append(np.log10(re_org / re_aq))
|
||||
pred = np.array(pred)
|
||||
meas = np.log10(exp_df['D(m)'].values)
|
||||
obj = np.sum((pred - meas) ** 2)
|
||||
return obj
|
||||
|
||||
def fit(self, kwargs) -> float:
|
||||
"""Fits experimental to modeled data by estimating complex reference enthalpy
|
||||
Returns estimated complex enthalpy in J/mol
|
||||
:param kwargs: (dict) parameters and options for scipy.minimize
|
||||
"""
|
||||
x_guess = self._x_guess
|
||||
h_guess = self._h_guess
|
||||
res = minimize(self.objective, x_guess, **kwargs)
|
||||
pred_complex_enthalpy = res.x[0] * h_guess / 1000 # J/mol
|
||||
return pred_complex_enthalpy
|
||||
|
||||
def update_xml(self,
|
||||
complex_enthalpy,
|
||||
phases_xml_filename=None,
|
||||
complex_name=None):
|
||||
"""updates xml file with new complex_enthalpy"""
|
||||
if phases_xml_filename is None:
|
||||
phases_xml_filename = self._phases_xml_filename
|
||||
if complex_name is None:
|
||||
complex_name = self._complex_name
|
||||
|
||||
tree = ET.parse(phases_xml_filename)
|
||||
root = tree.getroot()
|
||||
# Update xml file
|
||||
for species in root.iter('species'):
|
||||
if species.attrib['name'] is complex_name:
|
||||
for h0 in species.iter('h0'):
|
||||
h0.text = str(complex_enthalpy)
|
||||
h0.set('updated', 'Updated at ' + time.strftime('%X'))
|
||||
|
||||
tree.write(phases_xml_filename)
|
||||
|
||||
def parity_plot(self):
|
||||
"""Parity plot between measured and predicted rare earth composition"""
|
||||
phases_copy = self._phases.copy()
|
||||
mix = ct.Mixture(phases_copy)
|
||||
aq_ind = self._aq_ind
|
||||
exp_df = self._exp_df
|
||||
in_moles = self._in_moles
|
||||
rare_earth_ion_name = self._rare_earth_ion_name
|
||||
pred = []
|
||||
for row in in_moles.values:
|
||||
mix.species_moles = row
|
||||
mix.equilibrate('TP', log_level=0)
|
||||
re_aq = mix.species_moles[mix.species_index(
|
||||
aq_ind, rare_earth_ion_name)]
|
||||
pred.append(re_aq)
|
||||
pred = np.array(pred)
|
||||
meas = exp_df['REeq(m)'].values
|
||||
min_data = np.min([pred, meas])
|
||||
max_data = np.max([pred, meas])
|
||||
min_max_data = np.array([min_data, max_data])
|
||||
fig, ax = plt.subplots()
|
||||
sns.scatterplot(meas, pred, color="r")
|
||||
sns.lineplot(min_max_data, min_max_data, color="b")
|
||||
ax.set(xlabel='Measured X equilibrium', ylabel='Predicted X equilibrium')
|
||||
plt.show()
|
||||
return None
|
||||
|
||||
@@ -7,4 +7,12 @@ import json
|
||||
with open('one_comp_settings.txt') as file:
|
||||
testing_params = json.load(file)
|
||||
beaker = REEPS(**testing_params)
|
||||
print(beaker.get_in_moles()['Nd+++'])
|
||||
|
||||
minimizer_kwargs = {"method": 'SLSQP',
|
||||
"bounds": [(1e-1, 1e1)],
|
||||
"constraints": (),
|
||||
"options": {'disp': True, 'maxiter': 1000, 'ftol': 1e-6}}
|
||||
est_enthalpy = beaker.fit(minimizer_kwargs)
|
||||
print(est_enthalpy)
|
||||
beaker.update_xml(est_enthalpy)
|
||||
beaker.parity_plot()
|
||||
|
||||
Reference in New Issue
Block a user