Skip to content

Commit

Permalink
Rename Fit variables consistently, cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
Lemonexe committed Sep 22, 2024
1 parent 7400308 commit 4b8f74b
Show file tree
Hide file tree
Showing 11 changed files with 78 additions and 83 deletions.
2 changes: 1 addition & 1 deletion appPy/cli/fit.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
@click.command()
@click.argument('compound1')
@click.argument('compound2')
@click.option('-m', '--model', help=f'Which model to fit, enter one of: {model_list}')
@click.argument('model')
@click.option('-d', '--datasets', help='Comma-separated exact dataset names, otherwise do all datasets of the system')
@click.option('-p', '--params', help='Comma-separated initial parameters, will ignore params saved in file')
@click.option('-c', '--consts', help='Comma-separated names of parameters to be kept constant')
Expand Down
18 changes: 5 additions & 13 deletions appPy/src/fit/Fit.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,16 @@ def __init__(self, supported_models, model_name, params0, const_param_names=None
const_param_names (list of str): names of parameter names to be kept constant during optimization.
"""
super().__init__()
self.keys_to_serialize = [
'is_optimized', 'nparams', 'nparams0', 'RMS_init', 'RMS_final', 'AAD_init', 'AAD_final'
]
self.keys_to_serialize = ['nparams0', 'RMS0', 'AAD0']
self.supported_models = supported_models
self.is_optimized = False # whether optimization has been performed

self.model = get_model_by_name(supported_models, model_name)
self.params0, self.nparams0 = self.__parse_params0(params0) # initial params as np.array, and as dict
self.params = self.params0 # optimization result params as np.array
self.nparams = self.nparams0

self.const_param_names = self.__parse_const_param_names(const_param_names)

# initial and final objective function values
self.RMS_init = None
self.AAD_init = None
self.RMS_final = None
self.AAD_final = None
# initial objective function values
self.RMS0 = self.AAD0 = None

def set_named_params(self, params):
"""Compose vector of ordered params into an ordered dict of named params."""
Expand All @@ -44,12 +36,12 @@ def __parse_params0(self, params0):
params0: either ordered list of float, or named params as ordered key:value dict
"""
model = self.model
if not params0: return model.params0, dict(zip(model.param_names, model.params0))
if not params0: return model.params0, self.set_named_params(model.params0)
if isinstance(params0, dict):
nparams0 = params0
params0 = [params0[key] for key in model.param_names]
else:
nparams0 = dict(zip(model.param_names, params0))
nparams0 = self.set_named_params(params0)
if len(params0) != model.n_params:
raise AppException(f'{model.display_name} model expects {model.n_params} parameters, got {len(params0)}!')
return params0, nparams0
Expand Down
15 changes: 10 additions & 5 deletions appPy/src/fit/Fit_VLE.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def __init__(self, compound1, compound2, model_name, datasets, params0, const_pa
const_param_names (list of str): names of parameters to be kept constant during optimization.
"""
super().__init__(supported_models, model_name, params0, const_param_names)
self.keys_to_serialize.extend(['tabulated_datasets'])
self.keys_to_serialize.extend(['is_optimized', 'tabulated_datasets', 'nparams', 'RMS_final', 'AAD_final'])
self.compound1 = compound1
self.compound2 = compound2

Expand All @@ -43,8 +43,13 @@ def __init__(self, compound1, compound2, model_name, datasets, params0, const_pa
self.gamma_1 = squash(self.dataset_VLEs, 'gamma_1')
self.gamma_2 = squash(self.dataset_VLEs, 'gamma_2')

self.RMS_init = RMS(self.get_residual(self.params0))
self.AAD_init = AAD(self.get_residual(self.params0))
self.params = self.params0 # optimization result params as np.array
self.nparams = self.nparams0

self.is_optimized = False # whether optimization has been performed
self.RMS_final = self.AAD_final = None
self.RMS0 = RMS(self.get_residual(self.params0))
self.AAD0 = AAD(self.get_residual(self.params0))

def __check_model_suitability(self):
"""Check if the model is appropriate for given datasets."""
Expand Down Expand Up @@ -88,8 +93,8 @@ def report(self):
echo(f' {name} = {value:.4g}')

echo('')
echo(f'Initial RMS = {self.RMS_init:.3g}')
echo(f'Initial AAD = {self.AAD_init:.3g}')
echo(f'Initial RMS = {self.RMS0:.3g}')
echo(f'Initial AAD = {self.AAD0:.3g}')
if self.is_optimized:
echo(f'Final RMS = {self.RMS_final:.3g}')
echo(f'Final AAD = {self.AAD_final:.3g}')
Expand Down
57 changes: 29 additions & 28 deletions appPy/src/fit/Fit_Vapor.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,27 +20,28 @@ def __init__(self, compound, model_name, p_data, T_data, params0):
p_data (list of float): vector of pressure data.
T_data (list of float): vector of temperature data.
params0 (list of float): initial estimate of model parameters (ordered).
skip_T_p_optimization (bool): if True, only p residuals will be optimized.
"""
super().__init__(supported_models, model_name, params0)
self.keys_to_serialize.extend(
['is_T_p_optimized', 'odr_messages', 'RMS_inter', 'AAD_inter', 'nparams_inter', 'T_min', 'T_max'])
self.keys_to_serialize.extend([
'is_p_optimized', 'is_T_p_optimized', 'odr_messages', 'nparams_p', 'nparams_T_p', 'RMS_p', 'AAD_p',
'RMS_T_p', 'AAD_T_p', 'T_min', 'T_max'
])
self.compound = compound
self.p_data = np.array(p_data)
self.T_data = np.array(T_data)
# will be offered as model T_min & T_max
self.T_min = np.min(T_data)
self.T_max = np.max(T_data)

self.T_tab = self.p_tab_inter = self.p_tab_final = None
self.nparams_inter = self.params_inter = None
self.RMS_inter = self.AAD_inter = None
self.is_T_p_optimized = False # in case of Fit_Vapor, Fit.is_optimized means is_p_optimized
self.nparams_p = self.params_p = self.params_T_p = self.nparams_T_p = None
self.RMS_p = self.AAD_p = self.RMS_T_p = self.AAD_T_p = None
self.T_tab = self.p_tab_p = self.p_tab_T_p = None
self.is_p_optimized = self.is_T_p_optimized = False
self.odr_messages = [] # scipy.ODR has an unusual output format, as array of messages, but no status

# even for ODR, only p residuals are calculated for simplicity, so they may actually increase after ODR
self.RMS_init = RMS(self.get_p_residuals(self.params0))
self.AAD_init = AAD(self.get_p_residuals(self.params0))
self.RMS0 = RMS(self.get_p_residuals(self.params0))
self.AAD0 = AAD(self.get_p_residuals(self.params0))

def get_p_residuals(self, params):
"""With given params, calculate residuals only of the dependent variable (p)."""
Expand All @@ -55,15 +56,15 @@ def optimize_p(self):
if result.status <= 0:
self.warn(f'p-optimization failed! Status {result.status}: {result.message}')
return
self.is_optimized = True
self.params_inter = merge_params(result.x)
self.nparams_inter = self.set_named_params(self.params_inter)
self.RMS_inter = RMS(self.get_p_residuals(self.params_inter))
self.AAD_inter = AAD(self.get_p_residuals(self.params_inter))
self.is_p_optimized = True
self.params_p = merge_params(result.x)
self.nparams_p = self.set_named_params(self.params_p)
self.RMS_p = RMS(self.get_p_residuals(self.params_p))
self.AAD_p = AAD(self.get_p_residuals(self.params_p))

def optimize_T_p(self):
"""Optimize the model params, considering errors of both dependent (p) & independent (T) variables."""
full_params0 = self.params_inter if self.is_optimized else self.params # use intermediate params if available
full_params0 = self.params_p if self.is_p_optimized else self.params0 # use intermediate params if available
fun2wrap = lambda params, x: self.model.fun(x, *params) # swap the order of params and x
var_params0, wrapped_fun, merge_params = const_param_wrappers(fun2wrap, full_params0, self.const_param_names,
self.model.param_names)
Expand All @@ -76,19 +77,19 @@ def optimize_T_p(self):
self.warn(f'T,p-optimization failed with error: {e}')
return
self.is_T_p_optimized = True
self.params = merge_params(odr_result.beta)
self.nparams = self.set_named_params(self.params)
self.RMS_final = RMS(self.get_p_residuals(self.params))
self.AAD_final = AAD(self.get_p_residuals(self.params))
self.params_T_p = merge_params(odr_result.beta)
self.nparams_T_p = self.set_named_params(self.params_T_p)
self.RMS_T_p = RMS(self.get_p_residuals(self.params_T_p))
self.AAD_T_p = AAD(self.get_p_residuals(self.params_T_p))
self.odr_messages = odr_result.stopreason
self.odr_messages = self.odr_messages if isinstance(self.odr_messages, list) else [self.odr_messages]

def tabulate(self):
self.T_tab = np.linspace(self.T_min, self.T_max, cst.x_points_smooth_plot)
if self.is_optimized:
self.p_tab_inter = self.model.fun(self.T_tab, *self.params_inter)
if self.is_p_optimized:
self.p_tab_p = self.model.fun(self.T_tab, *self.params_p)
if self.is_T_p_optimized:
self.p_tab_final = self.model.fun(self.T_tab, *self.params)
self.p_tab_T_p = self.model.fun(self.T_tab, *self.params_T_p)

def report(self):
underline_echo(self.get_title())
Expand All @@ -98,14 +99,14 @@ def report(self):
echo('')

echo('Optimized parameters as intermediate & final value:')
for (name, p_inter, p_final) in zip(self.model.param_names, self.params_inter, self.params):
echo(f' {name} = {p_inter:7.4g} {p_final:9.4g}')
for (name, p_p, p_T_p) in zip(self.model.param_names, self.params_p, self.params_T_p):
echo(f' {name} = {p_p:7.4g} {p_T_p:9.4g}')

echo('')
echo(f'Initial RMS = {self.RMS_init:.3g}')
echo(f'Initial AAD = {self.AAD_init:.3g}')
echo(f'Final RMS = {self.RMS_final:.3g}')
echo(f'Final AAD = {self.AAD_final:.3g}')
echo(f'Initial RMS = {self.RMS0:.3g}')
echo(f'Initial AAD = {self.AAD0:.3g}')
echo(f'Final RMS = {self.RMS_T_p:.3g}')
echo(f'Final AAD = {self.AAD_T_p:.3g}')

def get_title(self):
return f'Regression of {self.model.display_name} on {self.compound}'
10 changes: 5 additions & 5 deletions appPy/src/plot/Fit_Vapor_plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ def decorate(self):

def plot_p(self, mode):
"""Overlay p-fitted model over vapor pressure data."""
if not self.is_optimized: return None
if not self.is_p_optimized: return None
init_plot(mode)
self.draw_data()

T_tab_disp = convert_T(self.T_tab)
p_tab_inter_disp = convert_p(self.p_tab_inter)
plt.plot(T_tab_disp, p_tab_inter_disp, '-k', label='p-fitted model')
p_tab_p_disp = convert_p(self.p_tab_p)
plt.plot(T_tab_disp, p_tab_p_disp, '-k', label='p-fitted model')

self.decorate()
return finish_plot(mode)
Expand All @@ -38,8 +38,8 @@ def plot_T_p(self, mode):
self.draw_data()

T_tab_disp = convert_T(self.T_tab)
p_tab_final_disp = convert_p(self.p_tab_final)
plt.plot(T_tab_disp, p_tab_final_disp, '-k', label='T,p-fitted model')
p_tab_T_p_disp = convert_p(self.p_tab_T_p)
plt.plot(T_tab_disp, p_tab_T_p_disp, '-k', label='T,p-fitted model')

self.decorate()
return finish_plot(mode)
2 changes: 1 addition & 1 deletion appUI/src/actions/FitVLE/FitVLEResultsDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export const FitVLEResultsDialog: FC<FitResultsDialogProps> = ({ open, handleClo
}, [data]);

const metricsSpreadsheetData = useMemo(() => {
const rows = [[data.RMS_init, data.AAD_init]];
const rows = [[data.RMS0, data.AAD0]];
if (optimized) rows.push([data.RMS_final!, data.AAD_final!]);
return makeReadOnly(spreadsheetToSigDgts(matrixToSpreadsheetData(rows), sigDgtsMetrics));
}, [data]);
Expand Down
12 changes: 6 additions & 6 deletions appUI/src/actions/FitVapor/FitVaporResultsDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,12 @@ export const FitVaporResultsDialog: FC<FitVaporResultsDialogProps> = ({
}) => {
const [infoOpen, setInfoOpen] = useState(false);

const optimizedP = data.is_optimized;
const optimizedP = data.is_p_optimized;
const optimizedTP = data.is_T_p_optimized;
const paramNames = useMemo(() => fromNamedParams(data.nparams0)[0], [data]);
const params0 = fromNamedParams(data.nparams0)[1];
const paramsP = fromNamedParams(data.nparams_inter)[1];
const paramsTP = fromNamedParams(data.nparams)[1];
const paramsP = fromNamedParams(data.nparams_p)[1];
const paramsTP = fromNamedParams(data.nparams_T_p)[1];

const rowLabels = useMemo(() => {
const rows = ['initial'];
Expand All @@ -49,9 +49,9 @@ export const FitVaporResultsDialog: FC<FitVaporResultsDialogProps> = ({
}, [data]);

const metricsSpreadsheetData = useMemo(() => {
const rows = [[data.RMS_init, data.AAD_init]];
if (optimizedP) rows.push([data.RMS_inter!, data.AAD_inter!]);
if (optimizedTP) rows.push([data.RMS_final!, data.AAD_final!]);
const rows = [[data.RMS0, data.AAD0]];
if (optimizedP) rows.push([data.RMS_p!, data.AAD_p!]);
if (optimizedTP) rows.push([data.RMS_T_p!, data.AAD_T_p!]);
return makeReadOnly(spreadsheetToSigDgts(matrixToSpreadsheetData(rows), sigDgtsMetrics));
}, [data]);

Expand Down
33 changes: 16 additions & 17 deletions appUI/src/adapters/api/types/fitTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,18 +44,17 @@ export type PlottedDataset = AnalysisResult & {
gamma_plot: string;
};

type FitMetrics = AnalysisResult & {
export type FitAnalysisResponse = AnalysisResult & {
is_optimized: boolean;
RMS_init: number;
RMS_final: number | null;
AAD_init: number;
AAD_final: number | null;
};
tabulated_datasets: PlottedDataset[];

export type FitAnalysisResponse = FitMetrics & {
nparams0: NamedParams;
nparams: NamedParams;
tabulated_datasets: PlottedDataset[];

RMS0: number;
RMS_final: number | null;
AAD0: number;
AAD_final: number | null;
};

export type FitTabulateRequest = SystemIdentifier & { model_name: string; p: number };
Expand All @@ -77,7 +76,7 @@ export type VaporFitRequest = CompoundIdentifier & {
};

export type VaporFitResponse = AnalysisResult & {
is_optimized: boolean;
is_p_optimized: boolean;
is_T_p_optimized: boolean;
odr_messages: string[];
T_min: number;
Expand All @@ -86,13 +85,13 @@ export type VaporFitResponse = AnalysisResult & {
plot_T_p?: string;

nparams0: NamedParams;
nparams_inter?: NamedParams;
nparams?: NamedParams;
nparams_p?: NamedParams;
nparams_T_p?: NamedParams;

RMS_init: number;
RMS_inter: number | null;
RMS_final: number | null;
AAD_init: number;
AAD_inter: number | null;
AAD_final: number | null;
RMS0: number;
RMS_p: number | null;
RMS_T_p: number | null;
AAD0: number;
AAD_p: number | null;
AAD_T_p: number | null;
};
1 change: 0 additions & 1 deletion dev/notes_VLizard.txt

This file was deleted.

6 changes: 3 additions & 3 deletions docs/appPy.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,11 @@ pipenv run cli\gamma CHF CHOL -d 25kPa --plot
pipenv run cli\rk CHF CHOL -d 25kPa,40kPa --plot
pipenv run cli\herington CHF CHOL -d 25kPa,40kPa
pipenv run cli\fredenslund CHF CHOL -d 25kPa --ge --res
pipenv run cli\fit CPF CPOL -m vanLaar -d 25kPa --txy --persist
pipenv run cli\fit CPF CPOL vanLaar -d 25kPa --txy --persist
pipenv run cli\tabulate CPOL CPF vanLaar 33 --txy
pipenv run cli\vn CPOL CPF vanLaar -d 25kPa --plot
pipenv run cli\fit CHOL CHF -d 10kPa,25kPa,40kPa --xy --txy --gamma -c c_12
pipenv run cli\fit EtOH H2O -m UNIQUAC --skip -d Kamihama2012,Voutsas2011 --xy --gamma -p 1.972,1.4,2.10547,0.92,2.0046,-2.4936,-728.9705,756.9477
pipenv run cli\fit CHOL CHF NRTL -d 10kPa,25kPa,40kPa --xy --txy --gamma -c c_12
pipenv run cli\fit EtOH H2O UNIQUAC --skip -d Kamihama2012,Voutsas2011 --xy --gamma -p 1.972,1.4,2.10547,0.92,2.0046,-2.4936,-728.9705,756.9477
```
See [appPy/cli](../appPy/cli), where filenames correspond to commands;
calling with `--help` will instruct you further.
Expand Down
5 changes: 2 additions & 3 deletions http/fit.http
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Accept: application/json
POST http://localhost:37137/fit/vle
Content-Type: application/json

{"compound1": "EtOH", "compound2": "H2O", "model_name": "vanLaar", "datasets": ["1939vac"],
{"compound1": "EtOH", "compound2": "H2O", "model_name": "vanLaar", "datasets": ["Voutsas2011"],
"skip_optimization": false, "nparams0": {"A_12": 0.4, "A_21": 0.1}}

###
Expand Down Expand Up @@ -35,8 +35,7 @@ Content-Type: application/json
"model_name": "Wagner",
"p_data": [9.98, 11.52, 14.39, 25.01, 25.95, 39.91, 40.05, 52.5, 60.41, 78.21, 89.63, 100.05],
"T_data": [363.95, 367.65, 373.55, 389.25, 390.25, 403.35, 403.55, 412.45, 417.15, 426.25, 431.25, 435.35],
"nparams0": {"A": -7.898313, "B": 1.997515, "C": -2.448405, "D": -1.880206, "T_c": 647, "p_c": 22071},
"skip_T_p_optimization": false
"nparams0": {"A": -7.898313, "B": 1.997515, "C": -2.448405, "D": -1.880206, "T_c": 647, "p_c": 22071}
}

###
Expand Down

0 comments on commit 4b8f74b

Please sign in to comment.