diff --git a/.github/workflows/typos.toml b/.github/workflows/typos.toml index 4900a7b560..13bcee5705 100644 --- a/.github/workflows/typos.toml +++ b/.github/workflows/typos.toml @@ -30,5 +30,11 @@ suh = "suh" ther = "ther" # KNO3 KNO = "KNO" +# Equil == equilibrium +equil = "equil" +# Ignore astroid package +astroid = "astroid" +# delt == delta +delt = "delt" # TEMPORARY - Remove after example update -upadate = "upadate" \ No newline at end of file +upadate = "upadate" diff --git a/README.md b/README.md index dbb5ba955f..5b60da1b02 100644 --- a/README.md +++ b/README.md @@ -50,23 +50,21 @@ You can check the version installed with the command: idaes --version ``` -Now install the examples and the pre-build extensions (binary solvers): +Now install the pre-built extensions (binary solvers): ```bash -idaes get-examples idaes get-extensions ``` -This will install the examples into an `examples` subdirectory which can be opened using a [Jypter](https://jupyter.org) Notebook: +The IDAES examples can be installed by running: ```bash -jupyter notebook examples/notebook_index.ipynb +pip install idaes-examples ``` -From there you can explore the examples and tutorials. -For more information on how to use Jupyter Lab, use the built-in *Help* menu and the extensive documentation on the [Jupyter website](https://jupyter.org). +For more information, refer to the [IDAES/examples](https://github.com/IDAES/examples) repository, as well as the online static version of the examples available at . -Finally, refer to the [complete idaes-pse documentation](https://idaes-pse.readthedocs.io/en/stable) for detailed [installation instructions](https://idaes-pse.readthedocs.io/en/stable/tutorials/getting_started/index.html), examples, guides, and reference. +Finally, refer to the [complete idaes-pse documentation](https://idaes-pse.readthedocs.io/en/latest) for detailed [installation instructions](https://idaes-pse.readthedocs.io/en/latest/tutorials/getting_started/index.html), examples, guides, and reference. ## System requirements diff --git a/data/resourcedb.json b/data/resourcedb.json index 682f02fccb..59fef8d3d7 100644 --- a/data/resourcedb.json +++ b/data/resourcedb.json @@ -1 +1 @@ -{"resources": {"1": {"id_": "3392197b265f4e5fa48586a88a5ab758", "type": "publication", "aliases": ["Pitzer:1984"], "collaborators": [], "created": 1644447428.100121, "modified": 1644447428.100121, "creator": {"name": "Dan Gunter"}, "data": {}, "datafiles": [{"desc": "Pitzer_1984.pdf", "path": "Pitzer_1984.pdf", "sha1": "be8c9c27fa8f698550b11469ce10ffdae758ac5c", "is_copy": true}], "datafiles_dir": "8d7b6e93a7a54fcf99d3a8bfe925ab19", "desc": "Thermodynamic Properties of Aqueous Sodium Chloride Solutions", "relations": [{"predicate": "derived", "identifier": "f34d3084108d409d9c1b3f24d28d7185", "role": "subject"}, {"predicate": "derived", "identifier": "ec2e8c2573d84843bde27ef41875bf04", "role": "subject"}, {"predicate": "derived", "identifier": "ece23e0e6bdd4967957b80e4aef79974", "role": "subject"}, {"predicate": "derived", "identifier": "efc8f11d85ea4b2581caee31fee28d32", "role": "subject"}, {"predicate": "derived", "identifier": "06ac7a2180e145ee8e7cfaf859e9aa59", "role": "subject"}, {"predicate": "derived", "identifier": "e2c0b319ea7f491f843968a744d32b74", "role": "subject"}, {"predicate": "derived", "identifier": "9efaf4c9339e41d2a4f2eb0e9284404e", "role": "subject"}, {"predicate": "derived", "identifier": "f8e02a257d234da68090c8beac17c70f", "role": "subject"}, {"predicate": "derived", "identifier": "f825d53229ac4002b1307eb016861b45", "role": "subject"}, {"predicate": "derived", "identifier": "a5d449b6bb0e4fad9c9731e7af475c11", "role": "subject"}, {"predicate": "derived", "identifier": "57b701ccc697451799fb2c16240544c9", "role": "subject"}, {"predicate": "derived", "identifier": "74ecfac36aea4ebdb5cc10052f46889e", "role": "subject"}, {"predicate": "derived", "identifier": "9792128dda1c443b9a0dcdcbbbf040f2", "role": "subject"}, {"predicate": "derived", "identifier": "f90a11d183ab4cdfb5e4baea0a483180", "role": "subject"}, {"predicate": "derived", "identifier": "0fcaedd4549a49428efb96edbe834bf0", "role": "subject"}, {"predicate": "derived", "identifier": "f2b833f5bd6c478f9a44f0dabef2d0ef", "role": "subject"}, {"predicate": "derived", "identifier": "7c3360acde714d6f83418582ad31f130", "role": "subject"}], "tags": [], "version_info": {"created": 1644447428.100121, "version": [0, 0, 0], "name": ""}, "sources": [{"date": "1984", "doi": "https://doi.org/10.1063/1.555709", "isbn": "", "language": "english", "source": "Kenneth S. Pitzer, J. Christopher Peiper, and R. H. Busey, \"Thermodynamic Properties of Aqueous Sodium Chloride Solutions\". Journal of Physical and Chemical Reference Data 13, 1 (1984)"}]}, "2": {"id_": "f34d3084108d409d9c1b3f24d28d7185", "type": "tabular_data", "aliases": ["Standard G"], "collaborators": [], "created": 1644447428.120092, "modified": 1644447428.120092, "creator": {"name": "Dan Gunter"}, "data": {"table_info": {"pitzer_1.csv": {"T": {"units": "C"}, "P": {"units": "bar"}, "G0/RT H2O": {"units": ""}, "G0/RT NaCl": {"units": ""}, "A phi": {"units": "(kg/mol)^0.5"}, "B0": {"units": "kg/mol"}, "B1": {"units": "kg/mol"}, "10^3xC": {"units": "(kg/mol)^2"}}}}, "datafiles": [{"desc": "Standard Gibbs energies. Debye-Huckel A_phi parameter, and virial coefficients for NaCl(aq)", "path": "pitzer_1.csv", "sha1": "7855c54732b86262fde172cdd60b417c919d50e3", "is_copy": true}], "datafiles_dir": "9d2a8a43c8134ba2b11235b0fb475a68", "desc": "Standard Gibbs energies. Debye-Huckel A_phi parameter, and virial coefficients for NaCl(aq)", "relations": [{"predicate": "derived", "identifier": "3392197b265f4e5fa48586a88a5ab758", "role": "object"}], "tags": ["Pitzer:1984"], "version_info": {"created": 1644447428.120092, "version": [0, 0, 0], "name": ""}}, "3": {"id_": "ec2e8c2573d84843bde27ef41875bf04", "type": "tabular_data", "aliases": ["Standard S"], "collaborators": [], "created": 1644447428.134073, "modified": 1644447428.134073, "creator": {"name": "Dan Gunter"}, "data": {"table_info": {"pitzer_2.csv": {"T": {"units": "C"}, "P": {"units": "bar"}, "S0/R H2O": {"units": ""}, "S0/R NaCl": {"units": ""}, "A_S/R": {"units": "(kg/mol)^0.5"}, "B0": {"units": "kg/mol/K"}, "B1": {"units": "kg/mol/K"}, "10^3xC": {"units": "(kg/mol)^2/K"}}}}, "datafiles": [{"desc": "Standard entropies, Debye-Huckel A_s parameter, and virial coefficients for the NaCl(aq) entropy", "path": "pitzer_2.csv", "sha1": "697c23df1a180648a2fa9f6d47891fd87b6edf67", "is_copy": true}], "datafiles_dir": "80e72419c7c243059dea2fbac2a991a7", "desc": "Standard entropies, Debye-Huckel A_s parameter, and virial coefficients for the NaCl(aq) entropy", "relations": [{"predicate": "derived", "identifier": "3392197b265f4e5fa48586a88a5ab758", "role": "object"}], "tags": ["Pitzer:1984"], "version_info": {"created": 1644447428.134073, "version": [0, 0, 0], "name": ""}}, "4": {"id_": "ece23e0e6bdd4967957b80e4aef79974", "type": "tabular_data", "aliases": ["Standard H"], "collaborators": [], "created": 1644447428.144003, "modified": 1644447428.144003, "creator": {"name": "Dan Gunter"}, "data": {"table_info": {"pitzer_3.csv": {"T": {"units": "C"}, "P": {"units": "bar"}, "H0/RT H2O": {"units": ""}, "H0/RT NaCl": {"units": ""}, "A_L/RT": {"units": "(kg/mol)^0.5"}, "B0": {"units": "kg/mol/K"}, "B1": {"units": "kg/mol/K"}, "10^3xC": {"units": "(kg/mol)^2/K"}}}}, "datafiles": [{"desc": "Standard enthalpies, Debye-Huckel A_L parameter and virial coefficients for the NaCl(aq) enthalpy", "path": "pitzer_3.csv", "sha1": "dc8587ac0d2635d7e010954e17efe7820726a519", "is_copy": true}], "datafiles_dir": "527176e4eb33410fa40cbdd155e57d60", "desc": "Standard enthalpies, Debye-Huckel A_L parameter and virial coefficients for the NaCl(aq) enthalpy", "relations": [{"predicate": "derived", "identifier": "3392197b265f4e5fa48586a88a5ab758", "role": "object"}], "tags": ["Pitzer:1984"], "version_info": {"created": 1644447428.144003, "version": [0, 0, 0], "name": ""}}, "5": {"id_": "efc8f11d85ea4b2581caee31fee28d32", "type": "tabular_data", "aliases": ["Standard Cp"], "collaborators": [], "created": 1644447428.154019, "modified": 1644447428.154019, "creator": {"name": "Dan Gunter"}, "data": {"table_info": {"pitzer_4.csv": {"T": {"units": "C"}, "P": {"units": "bar"}, "Cp0/R H2O": {"units": ""}, "Cp0/R NaCl": {"units": ""}, "A_J/R": {"units": "(kg/mol)^0.5"}, "B0": {"units": "kg/mol/K^2"}, "B1": {"units": "kg/mol/K^2"}, "10^3xC": {"units": "(kg/mol/K)^2"}}}}, "datafiles": [{"desc": "Standard heat capacities, Debye-Huckel A_J parameter, and virial coeffiCients for the NaCl(aq) heat capacity", "path": "pitzer_4.csv", "sha1": "4fa4c924a8ca7414f8dee4a623f74c2d6e88f826", "is_copy": true}], "datafiles_dir": "14b83b0763c5424b8a056f5b0821dc32", "desc": "Standard heat capacities, Debye-Huckel A_J parameter, and virial coeffiCients for the NaCl(aq) heat capacity", "relations": [{"predicate": "derived", "identifier": "3392197b265f4e5fa48586a88a5ab758", "role": "object"}], "tags": ["Pitzer:1984"], "version_info": {"created": 1644447428.154019, "version": [0, 0, 0], "name": ""}}, "6": {"id_": "06ac7a2180e145ee8e7cfaf859e9aa59", "type": "tabular_data", "aliases": ["Standard Volume"], "collaborators": [], "created": 1644447428.165946, "modified": 1644447428.165946, "creator": {"name": "Dan Gunter"}, "data": {"table_info": {"pitzer_5.csv": {"T": {"units": "C"}, "P": {"units": "bar"}, "V0 H2O": {"units": "cc/mol"}, "V0 NaCl": {"units": "cc/mol"}, "A_V": {"units": "cc^3/kg^0.5/mol^3/2"}, "1e6*B": {"units": "kg/mol.bar"}, "1e6*C": {"units": "kg^2/mol^2/bar^2"}}}}, "datafiles": [{"desc": "Standard volumes, Debye-Huckel A_v parameter, and virial coefficients for the NaCl(aq) volume", "path": "pitzer_5.csv", "sha1": "26b672c9c515978a24f80e6a682d8242851339c0", "is_copy": true}], "datafiles_dir": "47c37ea39cd04decae3a13cb89397255", "desc": "Standard volumes, Debye-Huckel A_v parameter, and virial coefficients for the NaCl(aq) volume", "relations": [{"predicate": "derived", "identifier": "3392197b265f4e5fa48586a88a5ab758", "role": "object"}], "tags": ["Pitzer:1984"], "version_info": {"created": 1644447428.165946, "version": [0, 0, 0], "name": ""}}, "7": {"id_": "e2c0b319ea7f491f843968a744d32b74", "type": "tabular_data", "aliases": ["Standard Expansivity"], "collaborators": [], "created": 1644447428.176168, "modified": 1644447428.176168, "creator": {"name": "Dan Gunter"}, "data": {"table_info": {"pitzer_6.csv": {"T": {"units": "C"}, "P": {"units": "bar"}, "1e3*dV0/dT H2O": {"units": "cc/mol/K"}, "dV0/dT NaCl": {"units": "cc/mol/K"}, "A_X": {"units": "Cc^3 kg^0.5/mol^3/2/K"}, "1e6*B": {"units": "kg/mol/bar/K"}, "1e9*C": {"units": "kg^2/mol^2/bar/K"}}}}, "datafiles": [{"desc": "Standard expansivities, Debye-Huckel A_x parameter, and virial coefficients for the NaCl(aq) expansivity", "path": "pitzer_6.csv", "sha1": "02ec98677ac4a51129b3be15b844355852ee2824", "is_copy": true}], "datafiles_dir": "d65aa0a466a24a4a9f5f7c9ef6809355", "desc": "Standard expansivities, Debye-Huckel A_x parameter, and virial coefficients for the NaCl(aq) expansivity", "relations": [{"predicate": "derived", "identifier": "3392197b265f4e5fa48586a88a5ab758", "role": "object"}], "tags": ["Pitzer:1984"], "version_info": {"created": 1644447428.176168, "version": [0, 0, 0], "name": ""}}, "8": {"id_": "9efaf4c9339e41d2a4f2eb0e9284404e", "type": "tabular_data", "aliases": ["Standard Compressibility"], "collaborators": [], "created": 1644447428.187425, "modified": 1644447428.187425, "creator": {"name": "Dan Gunter"}, "data": {"table_info": {"pitzer_7.csv": {"T": {"units": "C"}, "P": {"units": "bar"}, "1e3*dV0/dP H2O": {"units": "cc/mol/bar"}, "1e3*dV0/dP NaCl": {"units": "cc/mol/bar"}, "1e3*A_k": {"units": "Cc^3 kg^0.5/mol^3/2/bar"}, "1e9*B": {"units": "kg/mol/bar^2"}}}}, "datafiles": [{"desc": "Standard compressibilities, Debye-Huckel A_K parameter, and virial coefficients for the NaCl(aq) compressibility", "path": "pitzer_7.csv", "sha1": "9b10b30310e27671e9f1af57d6d82daa08644ff8", "is_copy": true}], "datafiles_dir": "f215996b34d24048891713a8d3a1be71", "desc": "Standard compressibilities, Debye-Huckel A_K parameter, and virial coefficients for the NaCl(aq) compressibility", "relations": [{"predicate": "derived", "identifier": "3392197b265f4e5fa48586a88a5ab758", "role": "object"}], "tags": ["Pitzer:1984"], "version_info": {"created": 1644447428.187425, "version": [0, 0, 0], "name": ""}}, "9": {"id_": "f8e02a257d234da68090c8beac17c70f", "type": "tabular_data", "aliases": ["Activity Coefficient"], "collaborators": [], "created": 1644447428.20149, "modified": 1644447428.20149, "creator": {"name": "Dan Gunter"}, "data": {"table_info": {"pitzer_8.csv": {"T": {"units": "\u00b0c"}, "P": {"units": "bar"}, "m=0.1": {"units": "mol/kg"}, "m=0.25": {"units": "mol/kg"}, "m=0.5": {"units": "mol/kg"}, "m=0.75": {"units": "mol/kg"}, "m=1.0": {"units": "mol/kg"}, "m=2.0": {"units": "mol/kg"}, "m=3.0": {"units": "mol/kg"}, "m=4.0": {"units": "mol/kg"}, "m=5.0": {"units": "mol/kg"}, "m=6.0": {"units": "mol/kg"}}}}, "datafiles": [{"desc": "Activity coefficient of NaCl(aq)", "path": "pitzer_8.csv", "sha1": "dbc37c277b179b34c1bb30c62cf1a21725aff8eb", "is_copy": true}], "datafiles_dir": "c160f86d2cf3439a9dfbe71f5ca468c4", "desc": "Activity coefficient of NaCl(aq)", "relations": [{"predicate": "derived", "identifier": "3392197b265f4e5fa48586a88a5ab758", "role": "object"}], "tags": ["Pitzer:1984"], "version_info": {"created": 1644447428.20149, "version": [0, 0, 0], "name": ""}}, "10": {"id_": "f825d53229ac4002b1307eb016861b45", "type": "tabular_data", "aliases": ["Osmotic Coefficient"], "collaborators": [], "created": 1644447428.213742, "modified": 1644447428.213742, "creator": {"name": "Dan Gunter"}, "data": {"table_info": {"pitzer_9.csv": {"T": {"units": "\u00b0c"}, "P": {"units": "bar"}, "m=0.1": {"units": "mol/kg"}, "m=0.25": {"units": "mol/kg"}, "m=0.5": {"units": "mol/kg"}, "m=0.75": {"units": "mol/kg"}, "m=1.0": {"units": "mol/kg"}, "m=2.0": {"units": "mol/kg"}, "m=3.0": {"units": "mol/kg"}, "m=4.0": {"units": "mol/kg"}, "m=5.0": {"units": "mol/kg"}, "m=6.0": {"units": "mol/kg"}}}}, "datafiles": [{"desc": "Osmotic coefficient of NaCl(aq)", "path": "pitzer_9.csv", "sha1": "d62dc6d62fdccfe15f0d47fa775319eae7cc3583", "is_copy": true}], "datafiles_dir": "c8bc9646005e45af98e94990648eef5f", "desc": "Osmotic coefficient of NaCl(aq)", "relations": [{"predicate": "derived", "identifier": "3392197b265f4e5fa48586a88a5ab758", "role": "object"}], "tags": ["Pitzer:1984"], "version_info": {"created": 1644447428.213742, "version": [0, 0, 0], "name": ""}}, "11": {"id_": "a5d449b6bb0e4fad9c9731e7af475c11", "type": "tabular_data", "aliases": ["Standard Entropy of Solution"], "collaborators": [], "created": 1644447428.225258, "modified": 1644447428.225258, "creator": {"name": "Dan Gunter"}, "data": {"table_info": {"pitzer_10.csv": {"T": {"units": "C"}, "Psat": {"units": "bar"}, "Psat or 1": {"units": "bar"}, "200": {"units": "bar"}, "400": {"units": "bar"}, "600": {"units": "bar"}, "800": {"units": "bar"}, "1000": {"units": "bar"}}}}, "datafiles": [{"desc": "Entropy of solution of NaCl(aq)", "path": "pitzer_10.csv", "sha1": "7a7f9477247690701192a53a2ff92d9e890b9bb7", "is_copy": true}], "datafiles_dir": "810b04b2994646c1bb5c188aa8f318af", "desc": "Entropy of solution of NaCl(aq)", "relations": [{"predicate": "derived", "identifier": "3392197b265f4e5fa48586a88a5ab758", "role": "object"}], "tags": ["Pitzer:1984"], "version_info": {"created": 1644447428.225258, "version": [0, 0, 0], "name": ""}}, "12": {"id_": "57b701ccc697451799fb2c16240544c9", "type": "tabular_data", "aliases": ["Standard Enthalpy of Solution"], "collaborators": [], "created": 1644447428.236212, "modified": 1644447428.236212, "creator": {"name": "Dan Gunter"}, "data": {"table_info": {"pitzer_11.csv": {"T": {"units": "C"}, "Psat": {"units": "bar"}, "Psat or 1": {"units": "bar"}, "200": {"units": "bar"}, "400": {"units": "bar"}, "600": {"units": "bar"}, "800": {"units": "bar"}, "1000": {"units": "bar"}}}}, "datafiles": [{"desc": "Enthalpy of solution of NaCl(aq)", "path": "pitzer_11.csv", "sha1": "702b6da2ed6e7b61507de592a8e94eec8b633a34", "is_copy": true}], "datafiles_dir": "fd1d039e4707443a9768f81a84c3ba85", "desc": "Enthalpy of solution of NaCl(aq)", "relations": [{"predicate": "derived", "identifier": "3392197b265f4e5fa48586a88a5ab758", "role": "object"}], "tags": ["Pitzer:1984"], "version_info": {"created": 1644447428.236212, "version": [0, 0, 0], "name": ""}}, "13": {"id_": "74ecfac36aea4ebdb5cc10052f46889e", "type": "tabular_data", "aliases": ["Excess Entropy"], "collaborators": [], "created": 1644447428.246817, "modified": 1644447428.246817, "creator": {"name": "Dan Gunter"}, "data": {"table_info": {"pitzer_12.csv": {"T": {"units": "\u00b0c"}, "P": {"units": "bar"}, "m=0.1": {"units": "mol/kg"}, "m=0.25": {"units": "mol/kg"}, "m=0.5": {"units": "mol/kg"}, "m=0.75": {"units": "mol/kg"}, "m=1.0": {"units": "mol/kg"}, "m=2.0": {"units": "mol/kg"}, "m=3.0": {"units": "mol/kg"}, "m=4.0": {"units": "mol/kg"}, "m=5.0": {"units": "mol/kg"}, "m=6.0": {"units": "mol/kg"}}}}, "datafiles": [{"desc": "Relative entropy of NaCl(aq)", "path": "pitzer_12.csv", "sha1": "53cdd93205c710d1c4a1490b440e35dcc1fcaed2", "is_copy": true}], "datafiles_dir": "81b031700e67446887efccafd43015da", "desc": "Relative entropy of NaCl(aq)", "relations": [{"predicate": "derived", "identifier": "3392197b265f4e5fa48586a88a5ab758", "role": "object"}], "tags": ["Pitzer:1984"], "version_info": {"created": 1644447428.246817, "version": [0, 0, 0], "name": ""}}, "14": {"id_": "9792128dda1c443b9a0dcdcbbbf040f2", "type": "tabular_data", "aliases": ["Excess Enthalpy"], "collaborators": [], "created": 1644447428.25895, "modified": 1644447428.25895, "creator": {"name": "Dan Gunter"}, "data": {"table_info": {"pitzer_13.csv": {"T": {"units": "\u00b0c"}, "P": {"units": "bar"}, "m=0.1": {"units": "mol/kg"}, "m=0.25": {"units": "mol/kg"}, "m=0.5": {"units": "mol/kg"}, "m=0.75": {"units": "mol/kg"}, "m=1.0": {"units": "mol/kg"}, "m=2.0": {"units": "mol/kg"}, "m=3.0": {"units": "mol/kg"}, "m=4.0": {"units": "mol/kg"}, "m=5.0": {"units": "mol/kg"}, "m=6.0": {"units": "mol/kg"}}}}, "datafiles": [{"desc": "Relative enthalpy of NaCl(aq)", "path": "pitzer_13.csv", "sha1": "bd6ccef9d9bd7767db706654944a5595de0f85af", "is_copy": true}], "datafiles_dir": "6136a7bd211c4a5683f6f389f9a762df", "desc": "Relative enthalpy of NaCl(aq)", "relations": [{"predicate": "derived", "identifier": "3392197b265f4e5fa48586a88a5ab758", "role": "object"}], "tags": ["Pitzer:1984"], "version_info": {"created": 1644447428.25895, "version": [0, 0, 0], "name": ""}}, "15": {"id_": "f90a11d183ab4cdfb5e4baea0a483180", "type": "tabular_data", "aliases": ["Excess Cp"], "collaborators": [], "created": 1644447428.27155, "modified": 1644447428.27155, "creator": {"name": "Dan Gunter"}, "data": {"table_info": {"pitzer_14.csv": {"T": {"units": "\u00b0c"}, "P": {"units": "bar"}, "m=0.1": {"units": "mol/kg"}, "m=0.25": {"units": "mol/kg"}, "m=0.5": {"units": "mol/kg"}, "m=0.75": {"units": "mol/kg"}, "m=1.0": {"units": "mol/kg"}, "m=2.0": {"units": "mol/kg"}, "m=3.0": {"units": "mol/kg"}, "m=4.0": {"units": "mol/kg"}, "m=5.0": {"units": "mol/kg"}, "m=6.0": {"units": "mol/kg"}}}}, "datafiles": [{"desc": "Relative heat capacity of NaCl(aq)", "path": "pitzer_14.csv", "sha1": "9c0df5fd212e27ccb6cb1bafc84ed0dd0699cbd7", "is_copy": true}], "datafiles_dir": "770e34089045464db1afad7f5cd384c7", "desc": "Relative heat capacity of NaCl(aq)", "relations": [{"predicate": "derived", "identifier": "3392197b265f4e5fa48586a88a5ab758", "role": "object"}], "tags": ["Pitzer:1984"], "version_info": {"created": 1644447428.27155, "version": [0, 0, 0], "name": ""}}, "16": {"id_": "0fcaedd4549a49428efb96edbe834bf0", "type": "tabular_data", "aliases": ["Density"], "collaborators": [], "created": 1644447428.281467, "modified": 1644447428.281467, "creator": {"name": "Dan Gunter"}, "data": {"table_info": {"pitzer_15.csv": {"T": {"units": "\u00b0c"}, "P": {"units": "bar"}, "m=0.1": {"units": "mol/kg"}, "m=0.25": {"units": "mol/kg"}, "m=0.5": {"units": "mol/kg"}, "m=0.75": {"units": "mol/kg"}, "m=1.0": {"units": "mol/kg"}, "m=2.0": {"units": "mol/kg"}, "m=3.0": {"units": "mol/kg"}, "m=4.0": {"units": "mol/kg"}, "m=5.0": {"units": "mol/kg"}, "m=6.0": {"units": "mol/kg"}}}}, "datafiles": [{"desc": "Density of NaCl(aq)", "path": "pitzer_15.csv", "sha1": "657c144d044fe6d2d127cd3ad52aeaaaf7ef756a", "is_copy": true}], "datafiles_dir": "fe744c9ba1f54e3a85f0f3e383951fc3", "desc": "Density of NaCl(aq)", "relations": [{"predicate": "derived", "identifier": "3392197b265f4e5fa48586a88a5ab758", "role": "object"}], "tags": ["Pitzer:1984"], "version_info": {"created": 1644447428.281467, "version": [0, 0, 0], "name": ""}}, "17": {"id_": "f2b833f5bd6c478f9a44f0dabef2d0ef", "type": "tabular_data", "aliases": ["Specific Entropy"], "collaborators": [], "created": 1644447428.293723, "modified": 1644447428.293723, "creator": {"name": "Dan Gunter"}, "data": {"table_info": {"pitzer_16.csv": {"T": {"units": "\u00b0c"}, "P": {"units": "bar"}, "m=0.1": {"units": "mol/kg"}, "m=0.25": {"units": "mol/kg"}, "m=0.5": {"units": "mol/kg"}, "m=0.75": {"units": "mol/kg"}, "m=1.0": {"units": "mol/kg"}, "m=2.0": {"units": "mol/kg"}, "m=3.0": {"units": "mol/kg"}, "m=4.0": {"units": "mol/kg"}, "m=5.0": {"units": "mol/kg"}, "m=6.0": {"units": "mol/kg"}}}}, "datafiles": [{"desc": "Specific entropy of NaCl(aq)", "path": "pitzer_16.csv", "sha1": "1c2da93916616079083a220c48cbad57a3189d13", "is_copy": true}], "datafiles_dir": "79865319d0fd4b328b4cf53af35301f7", "desc": "Specific entropy of NaCl(aq)", "relations": [{"predicate": "derived", "identifier": "3392197b265f4e5fa48586a88a5ab758", "role": "object"}], "tags": ["Pitzer:1984"], "version_info": {"created": 1644447428.293723, "version": [0, 0, 0], "name": ""}}, "18": {"id_": "7c3360acde714d6f83418582ad31f130", "type": "tabular_data", "aliases": ["Specific Enthalpy"], "collaborators": [], "created": 1644447428.306298, "modified": 1644447428.306298, "creator": {"name": "Dan Gunter"}, "data": {"table_info": {"pitzer_17.csv": {"T": {"units": "\u00b0c"}, "P": {"units": "bar"}, "m=0.1": {"units": "mol/kg"}, "m=0.25": {"units": "mol/kg"}, "m=0.5": {"units": "mol/kg"}, "m=0.75": {"units": "mol/kg"}, "m=1.0": {"units": "mol/kg"}, "m=2.0": {"units": "mol/kg"}, "m=3.0": {"units": "mol/kg"}, "m=4.0": {"units": "mol/kg"}, "m=5.0": {"units": "mol/kg"}, "m=6.0": {"units": "mol/kg"}}}}, "datafiles": [{"desc": "Specific enthalpy of NaCl(aq)", "path": "pitzer_17.csv", "sha1": "aea0c13f41f867ce3b83ce5c534170e1eb7d9904", "is_copy": true}], "datafiles_dir": "e5bffe45d9e54011ad7f45506cd943d9", "desc": "Specific enthalpy of NaCl(aq)", "relations": [{"predicate": "derived", "identifier": "3392197b265f4e5fa48586a88a5ab758", "role": "object"}], "tags": ["Pitzer:1984"], "version_info": {"created": 1644447428.306298, "version": [0, 0, 0], "name": ""}}}} \ No newline at end of file +{"resources": {"1": {"id_": "3392197b265f4e5fa48586a88a5ab758", "type": "publication", "aliases": ["Pitzer:1984"], "collaborators": [], "created": 1644447428.100121, "modified": 1644447428.100121, "creator": {"name": "Dan Gunter"}, "data": {}, "datafiles": [{"desc": "Pitzer_1984.pdf", "path": "Pitzer_1984.pdf", "sha1": "be8c9c27fa8f698550b11469ce10ffdae758ac5c", "is_copy": true}], "datafiles_dir": "8d7b6e93a7a54fcf99d3a8bfe925ab19", "desc": "Thermodynamic Properties of Aqueous Sodium Chloride Solutions", "relations": [{"predicate": "derived", "identifier": "f34d3084108d409d9c1b3f24d28d7185", "role": "subject"}, {"predicate": "derived", "identifier": "ec2e8c2573d84843bde27ef41875bf04", "role": "subject"}, {"predicate": "derived", "identifier": "ece23e0e6bdd4967957b80e4aef79974", "role": "subject"}, {"predicate": "derived", "identifier": "efc8f11d85ea4b2581caee31fee28d32", "role": "subject"}, {"predicate": "derived", "identifier": "06ac7a2180e145ee8e7cfaf859e9aa59", "role": "subject"}, {"predicate": "derived", "identifier": "e2c0b319ea7f491f843968a744d32b74", "role": "subject"}, {"predicate": "derived", "identifier": "9efaf4c9339e41d2a4f2eb0e9284404e", "role": "subject"}, {"predicate": "derived", "identifier": "f8e02a257d234da68090c8beac17c70f", "role": "subject"}, {"predicate": "derived", "identifier": "f825d53229ac4002b1307eb016861b45", "role": "subject"}, {"predicate": "derived", "identifier": "a5d449b6bb0e4fad9c9731e7af475c11", "role": "subject"}, {"predicate": "derived", "identifier": "57b701ccc697451799fb2c16240544c9", "role": "subject"}, {"predicate": "derived", "identifier": "74ecfac36aea4ebdb5cc10052f46889e", "role": "subject"}, {"predicate": "derived", "identifier": "9792128dda1c443b9a0dcdcbbbf040f2", "role": "subject"}, {"predicate": "derived", "identifier": "f90a11d183ab4cdfb5e4baea0a483180", "role": "subject"}, {"predicate": "derived", "identifier": "0fcaedd4549a49428efb96edbe834bf0", "role": "subject"}, {"predicate": "derived", "identifier": "f2b833f5bd6c478f9a44f0dabef2d0ef", "role": "subject"}, {"predicate": "derived", "identifier": "7c3360acde714d6f83418582ad31f130", "role": "subject"}], "tags": [], "version_info": {"created": 1644447428.100121, "version": [0, 0, 0], "name": ""}, "sources": [{"date": "1984", "doi": "https://doi.org/10.1063/1.555709", "isbn": "", "language": "english", "source": "Kenneth S. Pitzer, J. Christopher Peiper, and R. H. Busey, \"Thermodynamic Properties of Aqueous Sodium Chloride Solutions\". Journal of Physical and Chemical Reference Data 13, 1 (1984)"}]}, "2": {"id_": "f34d3084108d409d9c1b3f24d28d7185", "type": "tabular_data", "aliases": ["Standard G"], "collaborators": [], "created": 1644447428.120092, "modified": 1644447428.120092, "creator": {"name": "Dan Gunter"}, "data": {"table_info": {"pitzer_1.csv": {"T": {"units": "C"}, "P": {"units": "bar"}, "G0/RT H2O": {"units": ""}, "G0/RT NaCl": {"units": ""}, "A phi": {"units": "(kg/mol)^0.5"}, "B0": {"units": "kg/mol"}, "B1": {"units": "kg/mol"}, "10^3xC": {"units": "(kg/mol)^2"}}}}, "datafiles": [{"desc": "Standard Gibbs energies. Debye-Huckel A_phi parameter, and virial coefficients for NaCl(aq)", "path": "pitzer_1.csv", "sha1": "7855c54732b86262fde172cdd60b417c919d50e3", "is_copy": true}], "datafiles_dir": "9d2a8a43c8134ba2b11235b0fb475a68", "desc": "Standard Gibbs energies. Debye-Huckel A_phi parameter, and virial coefficients for NaCl(aq)", "relations": [{"predicate": "derived", "identifier": "3392197b265f4e5fa48586a88a5ab758", "role": "object"}], "tags": ["Pitzer:1984"], "version_info": {"created": 1644447428.120092, "version": [0, 0, 0], "name": ""}}, "3": {"id_": "ec2e8c2573d84843bde27ef41875bf04", "type": "tabular_data", "aliases": ["Standard S"], "collaborators": [], "created": 1644447428.134073, "modified": 1644447428.134073, "creator": {"name": "Dan Gunter"}, "data": {"table_info": {"pitzer_2.csv": {"T": {"units": "C"}, "P": {"units": "bar"}, "S0/R H2O": {"units": ""}, "S0/R NaCl": {"units": ""}, "A_S/R": {"units": "(kg/mol)^0.5"}, "B0": {"units": "kg/mol/K"}, "B1": {"units": "kg/mol/K"}, "10^3xC": {"units": "(kg/mol)^2/K"}}}}, "datafiles": [{"desc": "Standard entropies, Debye-Huckel A_s parameter, and virial coefficients for the NaCl(aq) entropy", "path": "pitzer_2.csv", "sha1": "697c23df1a180648a2fa9f6d47891fd87b6edf67", "is_copy": true}], "datafiles_dir": "80e72419c7c243059dea2fbac2a991a7", "desc": "Standard entropies, Debye-Huckel A_s parameter, and virial coefficients for the NaCl(aq) entropy", "relations": [{"predicate": "derived", "identifier": "3392197b265f4e5fa48586a88a5ab758", "role": "object"}], "tags": ["Pitzer:1984"], "version_info": {"created": 1644447428.134073, "version": [0, 0, 0], "name": ""}}, "4": {"id_": "ece23e0e6bdd4967957b80e4aef79974", "type": "tabular_data", "aliases": ["Standard H"], "collaborators": [], "created": 1644447428.144003, "modified": 1644447428.144003, "creator": {"name": "Dan Gunter"}, "data": {"table_info": {"pitzer_3.csv": {"T": {"units": "C"}, "P": {"units": "bar"}, "H0/RT H2O": {"units": ""}, "H0/RT NaCl": {"units": ""}, "A_L/RT": {"units": "(kg/mol)^0.5"}, "B0": {"units": "kg/mol/K"}, "B1": {"units": "kg/mol/K"}, "10^3xC": {"units": "(kg/mol)^2/K"}}}}, "datafiles": [{"desc": "Standard enthalpies, Debye-Huckel A_L parameter and virial coefficients for the NaCl(aq) enthalpy", "path": "pitzer_3.csv", "sha1": "dc8587ac0d2635d7e010954e17efe7820726a519", "is_copy": true}], "datafiles_dir": "527176e4eb33410fa40cbdd155e57d60", "desc": "Standard enthalpies, Debye-Huckel A_L parameter and virial coefficients for the NaCl(aq) enthalpy", "relations": [{"predicate": "derived", "identifier": "3392197b265f4e5fa48586a88a5ab758", "role": "object"}], "tags": ["Pitzer:1984"], "version_info": {"created": 1644447428.144003, "version": [0, 0, 0], "name": ""}}, "5": {"id_": "efc8f11d85ea4b2581caee31fee28d32", "type": "tabular_data", "aliases": ["Standard Cp"], "collaborators": [], "created": 1644447428.154019, "modified": 1644447428.154019, "creator": {"name": "Dan Gunter"}, "data": {"table_info": {"pitzer_4.csv": {"T": {"units": "C"}, "P": {"units": "bar"}, "Cp0/R H2O": {"units": ""}, "Cp0/R NaCl": {"units": ""}, "A_J/R": {"units": "(kg/mol)^0.5"}, "B0": {"units": "kg/mol/K^2"}, "B1": {"units": "kg/mol/K^2"}, "10^3xC": {"units": "(kg/mol/K)^2"}}}}, "datafiles": [{"desc": "Standard heat capacities, Debye-Huckel A_J parameter, and virial coefficients for the NaCl(aq) heat capacity", "path": "pitzer_4.csv", "sha1": "4fa4c924a8ca7414f8dee4a623f74c2d6e88f826", "is_copy": true}], "datafiles_dir": "14b83b0763c5424b8a056f5b0821dc32", "desc": "Standard heat capacities, Debye-Huckel A_J parameter, and virial coefficients for the NaCl(aq) heat capacity", "relations": [{"predicate": "derived", "identifier": "3392197b265f4e5fa48586a88a5ab758", "role": "object"}], "tags": ["Pitzer:1984"], "version_info": {"created": 1644447428.154019, "version": [0, 0, 0], "name": ""}}, "6": {"id_": "06ac7a2180e145ee8e7cfaf859e9aa59", "type": "tabular_data", "aliases": ["Standard Volume"], "collaborators": [], "created": 1644447428.165946, "modified": 1644447428.165946, "creator": {"name": "Dan Gunter"}, "data": {"table_info": {"pitzer_5.csv": {"T": {"units": "C"}, "P": {"units": "bar"}, "V0 H2O": {"units": "cc/mol"}, "V0 NaCl": {"units": "cc/mol"}, "A_V": {"units": "cc^3/kg^0.5/mol^3/2"}, "1e6*B": {"units": "kg/mol.bar"}, "1e6*C": {"units": "kg^2/mol^2/bar^2"}}}}, "datafiles": [{"desc": "Standard volumes, Debye-Huckel A_v parameter, and virial coefficients for the NaCl(aq) volume", "path": "pitzer_5.csv", "sha1": "26b672c9c515978a24f80e6a682d8242851339c0", "is_copy": true}], "datafiles_dir": "47c37ea39cd04decae3a13cb89397255", "desc": "Standard volumes, Debye-Huckel A_v parameter, and virial coefficients for the NaCl(aq) volume", "relations": [{"predicate": "derived", "identifier": "3392197b265f4e5fa48586a88a5ab758", "role": "object"}], "tags": ["Pitzer:1984"], "version_info": {"created": 1644447428.165946, "version": [0, 0, 0], "name": ""}}, "7": {"id_": "e2c0b319ea7f491f843968a744d32b74", "type": "tabular_data", "aliases": ["Standard Expansivity"], "collaborators": [], "created": 1644447428.176168, "modified": 1644447428.176168, "creator": {"name": "Dan Gunter"}, "data": {"table_info": {"pitzer_6.csv": {"T": {"units": "C"}, "P": {"units": "bar"}, "1e3*dV0/dT H2O": {"units": "cc/mol/K"}, "dV0/dT NaCl": {"units": "cc/mol/K"}, "A_X": {"units": "Cc^3 kg^0.5/mol^3/2/K"}, "1e6*B": {"units": "kg/mol/bar/K"}, "1e9*C": {"units": "kg^2/mol^2/bar/K"}}}}, "datafiles": [{"desc": "Standard expansivities, Debye-Huckel A_x parameter, and virial coefficients for the NaCl(aq) expansivity", "path": "pitzer_6.csv", "sha1": "02ec98677ac4a51129b3be15b844355852ee2824", "is_copy": true}], "datafiles_dir": "d65aa0a466a24a4a9f5f7c9ef6809355", "desc": "Standard expansivities, Debye-Huckel A_x parameter, and virial coefficients for the NaCl(aq) expansivity", "relations": [{"predicate": "derived", "identifier": "3392197b265f4e5fa48586a88a5ab758", "role": "object"}], "tags": ["Pitzer:1984"], "version_info": {"created": 1644447428.176168, "version": [0, 0, 0], "name": ""}}, "8": {"id_": "9efaf4c9339e41d2a4f2eb0e9284404e", "type": "tabular_data", "aliases": ["Standard Compressibility"], "collaborators": [], "created": 1644447428.187425, "modified": 1644447428.187425, "creator": {"name": "Dan Gunter"}, "data": {"table_info": {"pitzer_7.csv": {"T": {"units": "C"}, "P": {"units": "bar"}, "1e3*dV0/dP H2O": {"units": "cc/mol/bar"}, "1e3*dV0/dP NaCl": {"units": "cc/mol/bar"}, "1e3*A_k": {"units": "Cc^3 kg^0.5/mol^3/2/bar"}, "1e9*B": {"units": "kg/mol/bar^2"}}}}, "datafiles": [{"desc": "Standard compressibilities, Debye-Huckel A_K parameter, and virial coefficients for the NaCl(aq) compressibility", "path": "pitzer_7.csv", "sha1": "9b10b30310e27671e9f1af57d6d82daa08644ff8", "is_copy": true}], "datafiles_dir": "f215996b34d24048891713a8d3a1be71", "desc": "Standard compressibilities, Debye-Huckel A_K parameter, and virial coefficients for the NaCl(aq) compressibility", "relations": [{"predicate": "derived", "identifier": "3392197b265f4e5fa48586a88a5ab758", "role": "object"}], "tags": ["Pitzer:1984"], "version_info": {"created": 1644447428.187425, "version": [0, 0, 0], "name": ""}}, "9": {"id_": "f8e02a257d234da68090c8beac17c70f", "type": "tabular_data", "aliases": ["Activity Coefficient"], "collaborators": [], "created": 1644447428.20149, "modified": 1644447428.20149, "creator": {"name": "Dan Gunter"}, "data": {"table_info": {"pitzer_8.csv": {"T": {"units": "\u00b0c"}, "P": {"units": "bar"}, "m=0.1": {"units": "mol/kg"}, "m=0.25": {"units": "mol/kg"}, "m=0.5": {"units": "mol/kg"}, "m=0.75": {"units": "mol/kg"}, "m=1.0": {"units": "mol/kg"}, "m=2.0": {"units": "mol/kg"}, "m=3.0": {"units": "mol/kg"}, "m=4.0": {"units": "mol/kg"}, "m=5.0": {"units": "mol/kg"}, "m=6.0": {"units": "mol/kg"}}}}, "datafiles": [{"desc": "Activity coefficient of NaCl(aq)", "path": "pitzer_8.csv", "sha1": "dbc37c277b179b34c1bb30c62cf1a21725aff8eb", "is_copy": true}], "datafiles_dir": "c160f86d2cf3439a9dfbe71f5ca468c4", "desc": "Activity coefficient of NaCl(aq)", "relations": [{"predicate": "derived", "identifier": "3392197b265f4e5fa48586a88a5ab758", "role": "object"}], "tags": ["Pitzer:1984"], "version_info": {"created": 1644447428.20149, "version": [0, 0, 0], "name": ""}}, "10": {"id_": "f825d53229ac4002b1307eb016861b45", "type": "tabular_data", "aliases": ["Osmotic Coefficient"], "collaborators": [], "created": 1644447428.213742, "modified": 1644447428.213742, "creator": {"name": "Dan Gunter"}, "data": {"table_info": {"pitzer_9.csv": {"T": {"units": "\u00b0c"}, "P": {"units": "bar"}, "m=0.1": {"units": "mol/kg"}, "m=0.25": {"units": "mol/kg"}, "m=0.5": {"units": "mol/kg"}, "m=0.75": {"units": "mol/kg"}, "m=1.0": {"units": "mol/kg"}, "m=2.0": {"units": "mol/kg"}, "m=3.0": {"units": "mol/kg"}, "m=4.0": {"units": "mol/kg"}, "m=5.0": {"units": "mol/kg"}, "m=6.0": {"units": "mol/kg"}}}}, "datafiles": [{"desc": "Osmotic coefficient of NaCl(aq)", "path": "pitzer_9.csv", "sha1": "d62dc6d62fdccfe15f0d47fa775319eae7cc3583", "is_copy": true}], "datafiles_dir": "c8bc9646005e45af98e94990648eef5f", "desc": "Osmotic coefficient of NaCl(aq)", "relations": [{"predicate": "derived", "identifier": "3392197b265f4e5fa48586a88a5ab758", "role": "object"}], "tags": ["Pitzer:1984"], "version_info": {"created": 1644447428.213742, "version": [0, 0, 0], "name": ""}}, "11": {"id_": "a5d449b6bb0e4fad9c9731e7af475c11", "type": "tabular_data", "aliases": ["Standard Entropy of Solution"], "collaborators": [], "created": 1644447428.225258, "modified": 1644447428.225258, "creator": {"name": "Dan Gunter"}, "data": {"table_info": {"pitzer_10.csv": {"T": {"units": "C"}, "Psat": {"units": "bar"}, "Psat or 1": {"units": "bar"}, "200": {"units": "bar"}, "400": {"units": "bar"}, "600": {"units": "bar"}, "800": {"units": "bar"}, "1000": {"units": "bar"}}}}, "datafiles": [{"desc": "Entropy of solution of NaCl(aq)", "path": "pitzer_10.csv", "sha1": "7a7f9477247690701192a53a2ff92d9e890b9bb7", "is_copy": true}], "datafiles_dir": "810b04b2994646c1bb5c188aa8f318af", "desc": "Entropy of solution of NaCl(aq)", "relations": [{"predicate": "derived", "identifier": "3392197b265f4e5fa48586a88a5ab758", "role": "object"}], "tags": ["Pitzer:1984"], "version_info": {"created": 1644447428.225258, "version": [0, 0, 0], "name": ""}}, "12": {"id_": "57b701ccc697451799fb2c16240544c9", "type": "tabular_data", "aliases": ["Standard Enthalpy of Solution"], "collaborators": [], "created": 1644447428.236212, "modified": 1644447428.236212, "creator": {"name": "Dan Gunter"}, "data": {"table_info": {"pitzer_11.csv": {"T": {"units": "C"}, "Psat": {"units": "bar"}, "Psat or 1": {"units": "bar"}, "200": {"units": "bar"}, "400": {"units": "bar"}, "600": {"units": "bar"}, "800": {"units": "bar"}, "1000": {"units": "bar"}}}}, "datafiles": [{"desc": "Enthalpy of solution of NaCl(aq)", "path": "pitzer_11.csv", "sha1": "702b6da2ed6e7b61507de592a8e94eec8b633a34", "is_copy": true}], "datafiles_dir": "fd1d039e4707443a9768f81a84c3ba85", "desc": "Enthalpy of solution of NaCl(aq)", "relations": [{"predicate": "derived", "identifier": "3392197b265f4e5fa48586a88a5ab758", "role": "object"}], "tags": ["Pitzer:1984"], "version_info": {"created": 1644447428.236212, "version": [0, 0, 0], "name": ""}}, "13": {"id_": "74ecfac36aea4ebdb5cc10052f46889e", "type": "tabular_data", "aliases": ["Excess Entropy"], "collaborators": [], "created": 1644447428.246817, "modified": 1644447428.246817, "creator": {"name": "Dan Gunter"}, "data": {"table_info": {"pitzer_12.csv": {"T": {"units": "\u00b0c"}, "P": {"units": "bar"}, "m=0.1": {"units": "mol/kg"}, "m=0.25": {"units": "mol/kg"}, "m=0.5": {"units": "mol/kg"}, "m=0.75": {"units": "mol/kg"}, "m=1.0": {"units": "mol/kg"}, "m=2.0": {"units": "mol/kg"}, "m=3.0": {"units": "mol/kg"}, "m=4.0": {"units": "mol/kg"}, "m=5.0": {"units": "mol/kg"}, "m=6.0": {"units": "mol/kg"}}}}, "datafiles": [{"desc": "Relative entropy of NaCl(aq)", "path": "pitzer_12.csv", "sha1": "53cdd93205c710d1c4a1490b440e35dcc1fcaed2", "is_copy": true}], "datafiles_dir": "81b031700e67446887efccafd43015da", "desc": "Relative entropy of NaCl(aq)", "relations": [{"predicate": "derived", "identifier": "3392197b265f4e5fa48586a88a5ab758", "role": "object"}], "tags": ["Pitzer:1984"], "version_info": {"created": 1644447428.246817, "version": [0, 0, 0], "name": ""}}, "14": {"id_": "9792128dda1c443b9a0dcdcbbbf040f2", "type": "tabular_data", "aliases": ["Excess Enthalpy"], "collaborators": [], "created": 1644447428.25895, "modified": 1644447428.25895, "creator": {"name": "Dan Gunter"}, "data": {"table_info": {"pitzer_13.csv": {"T": {"units": "\u00b0c"}, "P": {"units": "bar"}, "m=0.1": {"units": "mol/kg"}, "m=0.25": {"units": "mol/kg"}, "m=0.5": {"units": "mol/kg"}, "m=0.75": {"units": "mol/kg"}, "m=1.0": {"units": "mol/kg"}, "m=2.0": {"units": "mol/kg"}, "m=3.0": {"units": "mol/kg"}, "m=4.0": {"units": "mol/kg"}, "m=5.0": {"units": "mol/kg"}, "m=6.0": {"units": "mol/kg"}}}}, "datafiles": [{"desc": "Relative enthalpy of NaCl(aq)", "path": "pitzer_13.csv", "sha1": "bd6ccef9d9bd7767db706654944a5595de0f85af", "is_copy": true}], "datafiles_dir": "6136a7bd211c4a5683f6f389f9a762df", "desc": "Relative enthalpy of NaCl(aq)", "relations": [{"predicate": "derived", "identifier": "3392197b265f4e5fa48586a88a5ab758", "role": "object"}], "tags": ["Pitzer:1984"], "version_info": {"created": 1644447428.25895, "version": [0, 0, 0], "name": ""}}, "15": {"id_": "f90a11d183ab4cdfb5e4baea0a483180", "type": "tabular_data", "aliases": ["Excess Cp"], "collaborators": [], "created": 1644447428.27155, "modified": 1644447428.27155, "creator": {"name": "Dan Gunter"}, "data": {"table_info": {"pitzer_14.csv": {"T": {"units": "\u00b0c"}, "P": {"units": "bar"}, "m=0.1": {"units": "mol/kg"}, "m=0.25": {"units": "mol/kg"}, "m=0.5": {"units": "mol/kg"}, "m=0.75": {"units": "mol/kg"}, "m=1.0": {"units": "mol/kg"}, "m=2.0": {"units": "mol/kg"}, "m=3.0": {"units": "mol/kg"}, "m=4.0": {"units": "mol/kg"}, "m=5.0": {"units": "mol/kg"}, "m=6.0": {"units": "mol/kg"}}}}, "datafiles": [{"desc": "Relative heat capacity of NaCl(aq)", "path": "pitzer_14.csv", "sha1": "9c0df5fd212e27ccb6cb1bafc84ed0dd0699cbd7", "is_copy": true}], "datafiles_dir": "770e34089045464db1afad7f5cd384c7", "desc": "Relative heat capacity of NaCl(aq)", "relations": [{"predicate": "derived", "identifier": "3392197b265f4e5fa48586a88a5ab758", "role": "object"}], "tags": ["Pitzer:1984"], "version_info": {"created": 1644447428.27155, "version": [0, 0, 0], "name": ""}}, "16": {"id_": "0fcaedd4549a49428efb96edbe834bf0", "type": "tabular_data", "aliases": ["Density"], "collaborators": [], "created": 1644447428.281467, "modified": 1644447428.281467, "creator": {"name": "Dan Gunter"}, "data": {"table_info": {"pitzer_15.csv": {"T": {"units": "\u00b0c"}, "P": {"units": "bar"}, "m=0.1": {"units": "mol/kg"}, "m=0.25": {"units": "mol/kg"}, "m=0.5": {"units": "mol/kg"}, "m=0.75": {"units": "mol/kg"}, "m=1.0": {"units": "mol/kg"}, "m=2.0": {"units": "mol/kg"}, "m=3.0": {"units": "mol/kg"}, "m=4.0": {"units": "mol/kg"}, "m=5.0": {"units": "mol/kg"}, "m=6.0": {"units": "mol/kg"}}}}, "datafiles": [{"desc": "Density of NaCl(aq)", "path": "pitzer_15.csv", "sha1": "657c144d044fe6d2d127cd3ad52aeaaaf7ef756a", "is_copy": true}], "datafiles_dir": "fe744c9ba1f54e3a85f0f3e383951fc3", "desc": "Density of NaCl(aq)", "relations": [{"predicate": "derived", "identifier": "3392197b265f4e5fa48586a88a5ab758", "role": "object"}], "tags": ["Pitzer:1984"], "version_info": {"created": 1644447428.281467, "version": [0, 0, 0], "name": ""}}, "17": {"id_": "f2b833f5bd6c478f9a44f0dabef2d0ef", "type": "tabular_data", "aliases": ["Specific Entropy"], "collaborators": [], "created": 1644447428.293723, "modified": 1644447428.293723, "creator": {"name": "Dan Gunter"}, "data": {"table_info": {"pitzer_16.csv": {"T": {"units": "\u00b0c"}, "P": {"units": "bar"}, "m=0.1": {"units": "mol/kg"}, "m=0.25": {"units": "mol/kg"}, "m=0.5": {"units": "mol/kg"}, "m=0.75": {"units": "mol/kg"}, "m=1.0": {"units": "mol/kg"}, "m=2.0": {"units": "mol/kg"}, "m=3.0": {"units": "mol/kg"}, "m=4.0": {"units": "mol/kg"}, "m=5.0": {"units": "mol/kg"}, "m=6.0": {"units": "mol/kg"}}}}, "datafiles": [{"desc": "Specific entropy of NaCl(aq)", "path": "pitzer_16.csv", "sha1": "1c2da93916616079083a220c48cbad57a3189d13", "is_copy": true}], "datafiles_dir": "79865319d0fd4b328b4cf53af35301f7", "desc": "Specific entropy of NaCl(aq)", "relations": [{"predicate": "derived", "identifier": "3392197b265f4e5fa48586a88a5ab758", "role": "object"}], "tags": ["Pitzer:1984"], "version_info": {"created": 1644447428.293723, "version": [0, 0, 0], "name": ""}}, "18": {"id_": "7c3360acde714d6f83418582ad31f130", "type": "tabular_data", "aliases": ["Specific Enthalpy"], "collaborators": [], "created": 1644447428.306298, "modified": 1644447428.306298, "creator": {"name": "Dan Gunter"}, "data": {"table_info": {"pitzer_17.csv": {"T": {"units": "\u00b0c"}, "P": {"units": "bar"}, "m=0.1": {"units": "mol/kg"}, "m=0.25": {"units": "mol/kg"}, "m=0.5": {"units": "mol/kg"}, "m=0.75": {"units": "mol/kg"}, "m=1.0": {"units": "mol/kg"}, "m=2.0": {"units": "mol/kg"}, "m=3.0": {"units": "mol/kg"}, "m=4.0": {"units": "mol/kg"}, "m=5.0": {"units": "mol/kg"}, "m=6.0": {"units": "mol/kg"}}}}, "datafiles": [{"desc": "Specific enthalpy of NaCl(aq)", "path": "pitzer_17.csv", "sha1": "aea0c13f41f867ce3b83ce5c534170e1eb7d9904", "is_copy": true}], "datafiles_dir": "e5bffe45d9e54011ad7f45506cd943d9", "desc": "Specific enthalpy of NaCl(aq)", "relations": [{"predicate": "derived", "identifier": "3392197b265f4e5fa48586a88a5ab758", "role": "object"}], "tags": ["Pitzer:1984"], "version_info": {"created": 1644447428.306298, "version": [0, 0, 0], "name": ""}}}} \ No newline at end of file diff --git a/docs/explanations/components/property_package/general/bubble_dew.rst b/docs/explanations/components/property_package/general/bubble_dew.rst index 5a8418f709..f569964fff 100644 --- a/docs/explanations/components/property_package/general/bubble_dew.rst +++ b/docs/explanations/components/property_package/general/bubble_dew.rst @@ -7,7 +7,7 @@ Bubble and Dew Point Methods Ideal Assumptions (``IdealBubbleDew``) -------------------------------------- -In the case where ideal behavior can be assumed, i.e. ideal gas assumption and Raoult's Law holds, the bubble and dew points can be calculated directly from the saturation pressure using the following equations. +In the case where ideal behavior can be assumed, i.e., ideal gas assumption and Raoult's Law holds, the bubble and dew points can be calculated directly from the saturation pressure using the following equations. Note that these equations assume that only two phases are present, and thus this approach is not easily extended to cases where additional phases are present (a ConfigurationError will be raised if this is detected). For systems with more than two phases, the LogBubbleDew approach should be used instead. Ideal Bubble Pressure ^^^^^^^^^^^^^^^^^^^^^ diff --git a/docs/explanations/components/property_package/general_reactions/equil_form.rst b/docs/explanations/components/property_package/general_reactions/equil_form.rst index 88e82a3ee9..a92c160337 100644 --- a/docs/explanations/components/property_package/general_reactions/equil_form.rst +++ b/docs/explanations/components/property_package/general_reactions/equil_form.rst @@ -7,7 +7,7 @@ Equilibrium Reaction Forms Power Law (power_law_equil) --------------------------- -The method uses a power law form using the concentration form provided to calculate the reaction rate. +The method uses a power law form using the concentration form provided to calculate the reaction equilibrium. .. math:: k_{eq} = \prod_{(p, j)}{x_{(p,j)}^{O_{(p,j)}}} @@ -18,14 +18,14 @@ The method uses a power law form using the concentration form provided to calcu ":math:`O`", "reaction_order", "phase, component", "Reaction order" -Providing a `reaction_order` dict is optional. If one is not provided, it will be assumed that this is an elementary reaction and that the reaction order is equal to the stoichiometric coefficient for all component in non-solid phases (the contribution of solid phases is assumed to be constant and included in the equilibrium constant, thus an order of zero is assumed). +Providing a `reaction_order` dict is optional. If one is not provided, it will be assumed that this is an elementary reaction and that the reaction order is equal to the stoichiometric coefficient for all components in non-solid phases (the contribution of solid phases is assumed to be constant and included in the equilibrium constant, thus an order of zero is assumed). Log Power Law (log_power_law_equil) ----------------------------------- -The method uses a log form of a power law using the concentration form provided to calculate the reaction rate. +The method uses a log form of a power law using the concentration form provided to calculate the reaction equilibrium. -.. math:: log(k_{eq}) = \sum_{(p, j)}{O_{(p,j)}*log(x_{(p,j))} +.. math:: log(k_{eq}) = \sum_{(p, j)}{O_{(p,j)}*log(x_{(p,j)})} **Parameters** @@ -34,4 +34,77 @@ The method uses a log form of a power law using the concentration form provided ":math:`O`", "reaction_order", "phase, component", "Reaction order" +Providing a `reaction_order` dict is optional. If one is not provided, it will be assumed that this is an elementary reaction and that the reaction order is equal to the stoichiometric coefficient for all components in non-solid phases (the contribution of solid phases is assumed to be constant and included in the equilibrium constant, thus an order of zero is assumed). + +Solubility Product (solubility_product) +--------------------------------------- + +The method uses a complementarity formulation for solid precipitation using solubility product form. + +For precipitation at equilibrium, + +.. math:: Q = k_{sp} - \prod_{(liq, j)}{x_{(liq,j)}^{O_{(liq,j)}}} + +* :math:`Q = 0` only holds if solids (S) are present in the system, :math:`S \geq 0`. +* :math:`Q \geq 0` if the system is subsaturated and no solids (S) are present, :math:`S = 0`. + +:math:`S` represents the solid presentation and is normalized and scaled as: + +.. math:: S = C*\frac{s}{s+N} + +where :math:`s` is assumed to be the sum of the flowrates of any solids formed in the reaction. + +Only one of :math:`Q` and :math:`S` can be greater than zero at any time, which can be written in the form of a complementarity constraint as: + +.. math:: Q - max(0, Q-S) == 0 + +The :math:`\max()` function is provided as an IDAES utility which provides a smooth max expression with mutable smoothing parameter, :math:`\epsilon`. + +**Parameters** + +.. csv-table:: + :header: "Symbol", "Parameter Name", "Indices", "Default", "Description" + + ":math:`O`", "reaction_order", "liq, component", "\-", "Reaction order" + ":math:`C`", "s_scale", "\-", "10", "Scaling factor for the solid term" + ":math:`N`", "s_norm", "\-", "1e-4 (`k_eq_ref` if provided)", "Normalization parameter for the solid term" + ":math:`\epsilon`", "eps", "\-", "1e-4", "Smoothing factor" + +Providing a `reaction_order` dict is optional. If one is not provided, it will be assumed that this is an elementary reaction and that the reaction order is equal to the stoichiometric coefficient for all components in non-solid phases (the contribution of solid phases is assumed to be constant and included in the equilibrium constant, thus an order of zero is assumed). + +Log Solubility Product (log_solubility_product) +----------------------------------------------- + +The method uses a complementarity formulation for solid precipitation using a log form of solubility product. + +For precipitation at equilibrium, + +.. math:: Q = log(k_{sp}) - \sum_{(liq, j)}{O_{(liq,j)}*log(x_{(liq,j)})} + +* :math:`Q = 0` only holds if solids (S) are present in the system, :math:`S \geq 0`. +* :math:`Q \geq 0` if the system is subsaturated and no solids (S) are present, :math:`S = 0`. + +:math:`S` represents the solid presentation and is normalized and scaled as: + +.. math:: S = C\frac{s}{s+N} + +where :math:`s` is assumed to be the sum of the flowrates of any solids formed in the reaction. + +Only one of :math:`Q` and :math:`S` can be greater than zero at any time, which can be written in the form of a complementarity constraint as: + +.. math:: Q - max(0, Q-S) == 0 + +The :math:`\max()` function is provided as an IDAES utility which provides a smooth max expression with mutable smoothing parameter, :math:`\epsilon`. + +**Parameters** + +.. csv-table:: + :header: "Symbol", "Parameter Name", "Indices", "Default", "Description" + + ":math:`O`", "reaction_order", "liq, component", "\-", "Reaction order" + ":math:`C`", "s_scale", "\-", "1", "Scaling factor for the solid term" + ":math:`N`", "s_norm", "\-", "1e-4 (`k_eq_ref` if provided)", "Normalization parameter for the solid term" + ":math:`\epsilon`", "eps", "\-", "1e-4", "Smoothing factor" + Providing a `reaction_order` dict is optional. If one is not provided, it will be assumed that this is an elementary reaction and that the reaction order is equal to the stoichiometric coefficient for all component in non-solid phases (the contribution of solid phases is assumed to be constant and included in the equilibrium constant, thus an order of zero is assumed). + diff --git a/docs/explanations/modeling_extensions/surrogate/api/alamopy/alamopy-options.rst b/docs/explanations/modeling_extensions/surrogate/api/alamopy/alamopy-options.rst deleted file mode 100644 index fdd0a6c65a..0000000000 --- a/docs/explanations/modeling_extensions/surrogate/api/alamopy/alamopy-options.rst +++ /dev/null @@ -1,204 +0,0 @@ -ALAMOPY.ALAMO Options -===================== - -This page lists in more detail the ALAMOPY options and the relation of ALAMO and ALAMOPY. - -.. contents:: - :depth: 3 - -Installing ALAMO ----------------- - -ALAMO (Automatic Learning of Algebraic MOdels) is an optional dependency developed and licensed by The Optimization Firm: https://www.minlp.com/alamo-modeling-tool. The provided link include further information on obtaining a license, installing the tool, obtaining the Baron (Branch-And-Reduce Optimization Navigator) solver which ALAMO leverages, and specific examples through a user manual and installation guide. Alternatively, users may directly access the user guide here: https://minlp.com/downloads/docs/alamo%20manual.pdf. - -During installations, it is recommended that users Windows 10 users check that the ALAMO path is set. Additionally, users must place the ALAMO license file in the folder where it is installed. - -More details on ALAMO options may be found in the user guide documentation linked above. If users encounter specific error codes while running the ALAMOPy tool in IDAES, the user guide contains detailed descriptions of each termination condition and error message. - -Basic ALAMOPY.ALAMO options ---------------------------- - -Data Arguments -^^^^^^^^^^^^^^ - -The following arguments are required by the `AlamoTrainer` method: - -* **input_labels**: user-specified labels given to the inputs -* **output_labels**: user-specified labels given to the outputs -* **training_dataframe**: dataframe (Pandas) object containing training dataset - -.. code-block:: python - - # after reading or generating a DataFrame object called `data_training` - trainer = AlamoTrainer(input_labels=['x1', 'x2'], output_labels=['z1', 'z2'], training_dataframe=data_training) - trainer.config.[Alamo Option] = [Valid Option Choice] # see below for more details - success, alm_surr, msg = trainer.train_surrogate() - -The following arguments are required by the `AlamoSurrogate` method: - -* **surrogate_expressions**: Pyomo expression object(s) generated by training the surrogate model(s) -* **input_labels**: user-specified labels given to the inputs -* **output_labels**: user-specified labels given to the outputs -* **input_bounds**: minimum/maximum bounds for each input variable to constraint training search space - -.. code-block:: python - - surrogate_expressions = trainer._results['Model'] - input_labels = trainer._input_labels - output_labels = trainer._output_labels - xmin, xmax = [0.1, 0.8], [0.8, 1.2] - input_bounds = {input_labels[i]: (xmin[i], xmax[i]) for i in range(len(input_labels))} - - alm_surr = AlamoSurrogate(surrogate_expressions, input_labels, output_labels, input_bounds) - -Available Basis Functions -^^^^^^^^^^^^^^^^^^^^^^^^^ - -The following basis functions are allowed during regression: - -* **constant, linfcns, expfcns, logfcns, sinfcns, cosfcns, grbfcns**: 0-1 option to include constant, linear, exponential, logarithmic, sine, cosine, and Gaussian radial basis functions. For example, - -.. code-block:: python - - trainer.config.constant = 1, trainer.config.linfcns = 1, trainer.config.expfcns = 1, trainer.config.logfcns = 1, trainer.config.sinfcns = 1, trainer.config.cosfcns = 1, trainer.config.grbfcns = 1 - - -This results in basis functions = k, x1, exp(x1), log(x1), sin(x1), cos(x1), exp(-(:math:`\epsilon` ||x1||)^2) - -* **rbfparam**: multiplicative constant :math:`epsilon` used in the Gaussian radial basis functions - -* **monomialpower, multi2power, multi3power**: list of monomial, binomial, and trinomial powers. For example, - -.. code-block:: python - - trainer.config.monomialpower = [2,3,4], trainer.config.multi2power = [1,2,3], trainer.config.multi3power = [1,2,3] - - -This results in the following basis functions: - - * Monomial functions = x^2, x^3, x^4 - * Binomial functions = x1*x2, (x1*x2)^2, (x1*x2)^3 - * Trinomial functions = (x1*x2*x3), (x1*x2*x3)^2, (x1*x2*x3)^3 - -* **ratiopower**: list of ratio powers. For example, - -.. code-block:: python - - trainer.config.ratiopower = (1,2,3) - -This results in basis functions = (x1/x2), (x1/x2)^2, (x1/x2)^3 - -ALAMO Regression Options -^^^^^^^^^^^^^^^^^^^^^^^^ - -* **modeler**: fitness metric to beused for model building (1-8) - - * 1. **BIC**: Bayesian information criterion - * 2. **MallowsCp**: Mallow's Cp - * 3. **AICc**: the corrected Akaike's information criterion - * 4. **HQC**: the Hannan-Quinn information criterion - * 5. **MSE**: mean square error - * 6. **SSEp**: sum of square error plus a penalty proportional to the model size (Note: convpen is the weight of the penalty) - * 7. **RIC**: the risk information criterion - * 8. **MADp**: the maximum absolute eviation plus a penalty proportional to model size (Note: convpen is the weight of the penalty) - -* **screener**: regularization method used to reduce the number of potential basis functions pre-optimization (0-2) - - * 0. **none**: don't use a regularization method - * 1. **lasso**: use the LASSO (Least Absolute Shrinkage and Selection Operator) regularization method - * 2. **SIS**: use the SIS (Sure Independence Screening) regularization method - -* **maxterms**: maximum number of terms to be fit in the model, surrogates will use fewer if possible -* **minterms**: minimum number of terms to be fit in the model, a value of 0 means no limit is imposed -* **convpen**: when MODELER is set to 6 or 8 the size of the model is weighted by CONVPEN. -* **sismult**: non-negative number of basis functions retained by the SIS screener -* **simulator**: a python function to be used as a simulator for ALAMO, a variable that is a python function (not a string) -* **maxiter**: max iteration of runs -* **maxtime**: max length of total execution time in seconds -* **datalimitterms**: limit model terms to number of measurements (True/False) -* **numlimitbasis**: eliminate infeasible basis functions (True/False) -* **exclude**: list of inputs to exclude during building -* **ignore**: list of outputs to ignore during building -* **xisint**: list of inputs that should be treated as integers -* **zisint**: list of outputs that should be treated as integers - -Scaling and Metrics Options -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -* **xfactor**: list of scaling factors for input variables -* **xscaling**: sets XFACTORS equal to the range of each input (True/False) -* **scalez**: scale output variables (True/False) -* **ncvf**: number of folds for cross validation -* **tolrelmetric**: relative tolerance for outputs -* **tolabsmetric**: absolute tolerance for outputs -* **tolmeanerror**: convergence tolerance for mean errors in outputs -* **tolsse**: absolute tolerance on SSE (sum of squared errors) -* **mipoptca**: absolute tolerance for MIP -* **mipoptcr**: relative tolerance for MIP -* **linearerror**: use a linear objective instead of squared error (True/False) -* **GAMS**: complete path to GAMS executable, or name if GAMS is in the user path -* **solvemip**: solve MIP with an optimizer (True/False) -* **GAMSSOLVER**: name of preferred GAMS solver to solve ALAMO mip quadratic subproblems -* **builder**: use a greedy heuristic (True/False) -* **backstepper**: use a greedy heuristicd to build down a model by starting from the least squares model and removing one variable at a time (True/False) - -File Options -^^^^^^^^^^^^ - -* **print_to_screen**: send ALAMO output to stdout (True/False) -* **alamo_path**: path to ALAMO executable (if not in path) -* **filename** : file name to use for ALAMO files, must be full path of a .alm file -* **working_directory**: full path to working directory for ALAMO to use -* **overwrite_files**: overwrite (delete) existing files when re-generating (True/False) - -ALAMOPY results dictionary ---------------------------- - -The results from alamopy.alamo are returned as a python dictionary. The data can be accessed by using the dictionary keys listed below. For example, - -.. code-block:: python - - # once the trainer object `trainer` has been defined, configured and trained - regression_results = trainer._results - surrogate_expressions = trainer._results['Model'] - -Fitness metrics -^^^^^^^^^^^^^^^ - -* **trainer._results['ModelSize']**: number of terms chosen in the regression -* **trainer._results['R2']**: R2 value of the regression -* **Objective value metrics**: trainer._results['SSE'], trainer._results['RMSE'], trainer._results['MADp'] - -Regression description -^^^^^^^^^^^^^^^^^^^^^^ - -* **trainer._results['AlamoVersion']**: Version of ALAMO -* **trainer._results['xlabels'], trainer._results['zlabels']**: The labels used for the inputs/outputs -* **trainer._results['xdata'], trainer._results['zdata']**: array of xdata/zdata -* **trainer._results['ninputs'], trainer._results['nbas']**: number of inputs/basis functions - -Performance specs -^^^^^^^^^^^^^^^^^ -There are three types of regression problems that are used: ordinary linear regression (olr), classic linear regression (clr), and a mixed integer program (mip). Performance metrics include the number of each problems and the time spent on each type of problem. Additionally, the time spent on other operations and the total time are included. - -* **trainer._results['numOLRs'], trainer._results['OLRtime'], trainer._results['numCLRs'], trainer._results['CLRtime'], trainer._results['numMIPs'], trainer._results['MIPtime']**: number of type of regression problems solved and time -* **trainer._results['OtherTime**: Time spent on other operations -* **trainer._results['TotalTime']**: Total time spent on the regression - -Custom Basis Functions -^^^^^^^^^^^^^^^^^^^^^^ - -Custom basis functions can be added to the built-in functions to expand the functional forms available. In ALAMO, this can be done with the following syntax - -.. code-block:: python - - NCUSTOMBAS # - BEGIN_CUSTOMBAS - x1^2 * x2^2 - END_CUSTOMBAS - -To use this advanced capability in ALAMOPY, the following function is called. Note it is necessary to use the xlabels assigned to the input parameters. - -.. code-block:: python - - trainer.config.custom_basis_functions = ["x1^2 * x2^2", "...", "..." ...] \ No newline at end of file diff --git a/docs/explanations/modeling_extensions/surrogate/api/alamopy/index.rst b/docs/explanations/modeling_extensions/surrogate/api/alamopy/index.rst index 539ce13167..c689ae299a 100644 --- a/docs/explanations/modeling_extensions/surrogate/api/alamopy/index.rst +++ b/docs/explanations/modeling_extensions/surrogate/api/alamopy/index.rst @@ -8,10 +8,17 @@ ALAMOPY: ALAMO Python .. toctree:: :maxdepth: 1 - alamopy-options - The purpose of ALAMOPY (Automatic Learning of Algebraic MOdels PYthon wrapper) is to provide a wrapper for the software ALAMO which generates algebraic surrogate models of black-box systems for which a simulator or experimental setup is available. Consider a system for which the outputs **z** are an unknown function **f** of the system inputs **x**. The software identifies a function **f**, i.e., a relationship between the inputs and outputs of the system, that best matches data (pairs of **x** and corresponding **z** values) that are collected via simulation or experimentation. +Installing ALAMO +---------------- + +ALAMO (Automatic Learning of Algebraic MOdels) is an optional dependency developed and licensed by The Optimization Firm: https://www.minlp.com/alamo-modeling-tool. The provided link include further information on obtaining a license, installing the tool, obtaining the Baron (Branch-And-Reduce Optimization Navigator) solver which ALAMO leverages, and specific examples through a user manual and installation guide. Alternatively, users may directly access the user guide here: https://minlp.com/downloads/docs/alamo%20manual.pdf. + +During installations, it is recommended that users Windows 10 users check that the ALAMO path is set. Additionally, users must place the ALAMO license file in the folder where it is installed. + +More details on ALAMO options may be found in the user guide documentation linked above. If users encounter specific error codes while running the ALAMOPy tool in IDAES, the user guide contains detailed descriptions of each termination condition and error message. + Basic Usage ----------- @@ -46,47 +53,197 @@ User may save their trained surrogate objects by serializing to JSON, and load i # to load a model surrogate = AlamoSurrogate.load_from_file('alamo_surrogate.json') -Options for *alamopy.AlamoTrainer* -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Data Arguments +^^^^^^^^^^^^^^ -.. rubric:: ALAMOPY.ALAMO Options +The following arguments are required by the `AlamoTrainer` method: -Below are some common arguments users may pass to ALAMO through `alamopy.AlamoTrainer` that govern the behavior of the regression procedure, solver, file characteristics and other options. See :ref: `ALAMOPY.ALAMO Options` for more details on valid options and values. +* **input_labels**: user-specified labels given to the inputs +* **output_labels**: user-specified labels given to the outputs +* **training_dataframe**: dataframe (Pandas) object containing training dataset -* input_labels - list of strings to label the input variables -* output_labels - list of strings to label the output variables -* logfcns, expfcns, cosfcns, sinfcns, linfcns, constant - these are '0-1' options to activate these functions -* monomialpower, multi2power, multi3power, ratiopower - list of terms to be used in the respective basis functions -* maxterms - maximum number of basis terms allowed in the "best fit" model -* filename - path to ALAMO .alm input file -* overwrite_files - True/False flag whether to overwrite files -* modeler - integer 1-8 determines the choice of fitness metric -* screener - integer 0-2 determines if and how the size of the model will be minimized during training -* solvemip - '0-1' option that will force the solving of the .gms file +.. code-block:: python -Visualization -------------- + # after reading or generating a DataFrame object called `data_training` + trainer = AlamoTrainer(input_labels=['x1', 'x2'], output_labels=['z1', 'z2'], training_dataframe=data_training) + trainer.config.[Alamo Option] = [Valid Option Choice] # see below for more details + success, alm_surr, msg = trainer.train_surrogate() -.. rubric:: Visualizing Surrogate Model Results +The following arguments are required by the `AlamoSurrogate` method: -For visualizing ALAMO-trained surrogates via parity and residual plots, see :ref:`Visualizing Surrogate Model Results`. +* **surrogate_expressions**: Pyomo expression object(s) generated by training the surrogate model(s) +* **input_labels**: user-specified labels given to the inputs +* **output_labels**: user-specified labels given to the outputs +* **input_bounds**: minimum/maximum bounds for each input variable to constraint training search space + +.. code-block:: python + + surrogate_expressions = trainer._results['Model'] + input_labels = trainer._input_labels + output_labels = trainer._output_labels + xmin, xmax = [0.1, 0.8], [0.8, 1.2] + input_bounds = {input_labels[i]: (xmin[i], xmax[i]) for i in range(len(input_labels))} + + alm_surr = AlamoSurrogate(surrogate_expressions, input_labels, output_labels, input_bounds) + +Available Basis Functions +^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following basis functions are allowed during regression: + +* **constant, linfcns, expfcns, logfcns, sinfcns, cosfcns**: 0-1 option to include constant, linear, exponential, logarithmic, sine, and cosine basis functions. For example, + +.. code-block:: python + + trainer.config.constant = 1, trainer.config.linfcns = 1, trainer.config.expfcns = 1, trainer.config.logfcns = 1, trainer.config.sinfcns = 1, trainer.config.cosfcns = 1 + + +This results in basis functions = k, x1, exp(x1), log(x1), sin(x1), cos(x1) + +* **monomialpower, multi2power, multi3power**: list of monomial, binomial, and trinomial powers. For example, + +.. code-block:: python + + trainer.config.monomialpower = [2,3,4], trainer.config.multi2power = [1,2,3], trainer.config.multi3power = [1,2,3] + + +This results in the following basis functions: + + * Monomial functions = x^2, x^3, x^4 + * Binomial functions = x1*x2, (x1*x2)^2, (x1*x2)^3 + * Trinomial functions = (x1*x2*x3), (x1*x2*x3)^2, (x1*x2*x3)^3 + +* **ratiopower**: list of ratio powers. For example, + +.. code-block:: python + + trainer.config.ratiopower = (1,2,3) + +This results in basis functions = (x1/x2), (x1/x2)^2, (x1/x2)^3 + +ALAMO Regression Options +^^^^^^^^^^^^^^^^^^^^^^^^ + +* **modeler**: fitness metric to beused for model building (1-8) + + * 1. **BIC**: Bayesian information criterion + * 2. **MallowsCp**: Mallow's Cp + * 3. **AICc**: the corrected Akaike's information criterion + * 4. **HQC**: the Hannan-Quinn information criterion + * 5. **MSE**: mean square error + * 6. **SSEp**: sum of square error plus a penalty proportional to the model size (Note: convpen is the weight of the penalty) + * 7. **RIC**: the risk information criterion + * 8. **MADp**: the maximum absolute eviation plus a penalty proportional to model size (Note: convpen is the weight of the penalty) + +* **screener**: regularization method used to reduce the number of potential basis functions pre-optimization (0-2) + + * 0. **none**: don't use a regularization method + * 1. **lasso**: use the LASSO (Least Absolute Shrinkage and Selection Operator) regularization method + * 2. **SIS**: use the SIS (Sure Independence Screening) regularization method + +* **maxterms**: maximum number of terms to be fit in the model, surrogates will use fewer if possible +* **minterms**: minimum number of terms to be fit in the model, a value of 0 means no limit is imposed +* **convpen**: when MODELER is set to 6 or 8 the size of the model is weighted by CONVPEN. +* **sismult**: non-negative number of basis functions retained by the SIS screener +* **simulator**: a python function to be used as a simulator for ALAMO, a variable that is a python function (not a string) +* **maxiter**: max iteration of runs +* **maxtime**: max length of total execution time in seconds +* **datalimitterms**: limit model terms to number of measurements (True/False) +* **numlimitbasis**: eliminate infeasible basis functions (True/False) +* **exclude**: list of inputs to exclude during building +* **ignore**: list of outputs to ignore during building +* **xisint**: list of inputs that should be treated as integers +* **zisint**: list of outputs that should be treated as integers + +Scaling and Metrics Options +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +* **xfactor**: list of scaling factors for input variables +* **xscaling**: sets XFACTORS equal to the range of each input (True/False) +* **scalez**: scale output variables (True/False) +* **ncvf**: number of folds for cross validation +* **tolrelmetric**: relative tolerance for outputs +* **tolabsmetric**: absolute tolerance for outputs +* **tolmeanerror**: convergence tolerance for mean errors in outputs +* **tolsse**: absolute tolerance on SSE (sum of squared errors) +* **mipoptca**: absolute tolerance for MIP +* **mipoptcr**: relative tolerance for MIP +* **linearerror**: use a linear objective instead of squared error (True/False) +* **GAMS**: complete path to GAMS executable, or name if GAMS is in the user path +* **solvemip**: solve MIP with an optimizer (True/False) +* **GAMSSOLVER**: name of preferred GAMS solver to solve ALAMO mip quadratic subproblems +* **builder**: use a greedy heuristic (True/False) +* **backstepper**: use a greedy heuristicd to build down a model by starting from the least squares model and removing one variable at a time (True/False) + +File Options +^^^^^^^^^^^^ + +* **print_to_screen**: send ALAMO output to stdout (True/False) +* **alamo_path**: path to ALAMO executable (if not in path) +* **filename** : file name to use for ALAMO files, must be full path of a .alm file +* **working_directory**: full path to working directory for ALAMO to use +* **overwrite_files**: overwrite (delete) existing files when re-generating (True/False) + +ALAMOPY results dictionary +-------------------------- + +The results from alamopy.alamo are returned as a python dictionary. The data can be accessed by using the dictionary keys listed below. For example, + +.. code-block:: python + + # once the trainer object `trainer` has been defined, configured and trained + regression_results = trainer._results + surrogate_expressions = trainer._results['Model'] + +Fitness metrics +^^^^^^^^^^^^^^^ + +* **trainer._results['ModelSize']**: number of terms chosen in the regression +* **trainer._results['R2']**: R2 value of the regression +* **Objective value metrics**: trainer._results['SSE'], trainer._results['RMSE'], trainer._results['MADp'] + +Regression description +^^^^^^^^^^^^^^^^^^^^^^ + +* **trainer._results['AlamoVersion']**: Version of ALAMO +* **trainer._results['xlabels'], trainer._results['zlabels']**: The labels used for the inputs/outputs +* **trainer._results['xdata'], trainer._results['zdata']**: array of xdata/zdata +* **trainer._results['ninputs'], trainer._results['nbas']**: number of inputs/basis functions + +Performance Metrics +^^^^^^^^^^^^^^^^^^^ +There are three types of regression problems that are used: ordinary linear regression (olr), classic linear regression (clr), and a mixed integer program (mip). Performance metrics include the number of each problems and the time spent on each type of problem. Additionally, the time spent on other operations and the total time are included. + +* **trainer._results['numOLRs'], trainer._results['OLRtime'], trainer._results['numCLRs'], trainer._results['CLRtime'], trainer._results['numMIPs'], trainer._results['MIPtime']**: number of type of regression problems solved and time +* **trainer._results['OtherTime**: Time spent on other operations +* **trainer._results['TotalTime']**: Total time spent on the regression Custom Basis Functions ---------------------- -Similar to ALAMO, there are advanced capabilities for customization and constrained regression facilitated by methods in ALAMOPY including custom basis functions. These methods interact with the regression using the ALAMO option file. +Custom basis functions can be added to the built-in functions to expand the functional forms available. In ALAMO, this can be done with the following syntax -Custom basis functions can be added to the built-in functions to expand the functional forms available. To use this advanced capability in ALAMOPY, the following function is called. Note it is necessary to use the xlabels assigned to the input parameters. +.. code-block:: python + + NCUSTOMBAS # + BEGIN_CUSTOMBAS + x1^2 * x2^2 + END_CUSTOMBAS + +To use this advanced capability in ALAMOPY, the following function is called. Note it is necessary to use the xlabels assigned to the input parameters. .. code-block:: python trainer.config.custom_basis_functions = ["x1^2 * x2^2", "...", "..." ...] -.. rubric:: ALAMOPY: ALAMO Python +Visualization +------------- -The prior form of ALAMOPY contains several advanced capabilities that are not yet supported by the new framework. See :ref: `ALAMOPY.ALAMO Options` for more details on these methods. +.. rubric:: Visualizing Surrogate Model Results + +For visualizing ALAMO-trained surrogates via parity and residual plots, see :ref:`Visualizing Surrogate Model Results`. ALAMOPY Examples ---------------- -For an example of optimizing a flowsheet containing an ALAMO-trained surrogate model, see [Autothermal Reformer Flowsheet Optimization with ALAMO Surrogate Object](https://github.com/IDAES/examples-pse/blob/main/src/Examples/SurrMod/FlowsheetOptimization/ALAMO_flowsheet_optimization.ipynb). \ No newline at end of file +For an example of optimizing a flowsheet containing an ALAMO-trained surrogate model, see [Autothermal Reformer Flowsheet Optimization with ALAMO Surrogate Object](https://github.com/IDAES/examples-pse/blob/main/src/Examples/SurrMod/FlowsheetOptimization/ALAMO_flowsheet_optimization.ipynb). diff --git a/docs/reference_guides/core/util/index.rst b/docs/reference_guides/core/util/index.rst index 806a290e23..384bbf3a50 100644 --- a/docs/reference_guides/core/util/index.rst +++ b/docs/reference_guides/core/util/index.rst @@ -6,6 +6,7 @@ dyn_utils initialization + math misc model_diagnostics model_serializer diff --git a/docs/reference_guides/core/util/math.rst b/docs/reference_guides/core/util/math.rst new file mode 100644 index 0000000000..d0864d1e81 --- /dev/null +++ b/docs/reference_guides/core/util/math.rst @@ -0,0 +1,13 @@ +Mathematical Utility Methods +============================ + +.. contents:: Contents + :depth: 2 + +There are a number of useful mathematical operations that either exhibit singularities or non-smooth behavior that can cause issues when used in equation-oriented models. This library contains smooth formulations for some of these operations which can be used to approximate these operations in models. + +Users should note that these formulations involve smoothing parameters which should be tuned for their particular system. Larger values generally result in smoother behavior but greater error. + +.. automodule:: idaes.core.util.math + :members: + diff --git a/docs/reference_guides/core/util/model_statistics.rst b/docs/reference_guides/core/util/model_statistics.rst index 0c238ced2f..52b4f8af5a 100644 --- a/docs/reference_guides/core/util/model_statistics.rst +++ b/docs/reference_guides/core/util/model_statistics.rst @@ -8,7 +8,7 @@ The most commonly used methods are ``degrees_of_freedom`` and ``report_statistic Degrees of Freedom Method ------------------------- -The ``degrees_of_freedom`` method calculates the number of degrees of freedom available in a given model. The calcuation is based on the number of unfixed variables which appear in active constraints, minus the number of active equality constraints in the model. Users should note that this method does not consider inequality or deactivated constraints, or variables which do not appear in active equality constraints. +The ``degrees_of_freedom`` method calculates the number of degrees of freedom available in a given model. The calculation is based on the number of unfixed variables which appear in active constraints, minus the number of active equality constraints in the model. Users should note that this method does not consider inequality or deactivated constraints, or variables which do not appear in active equality constraints. .. autofunction:: idaes.core.util.model_statistics.degrees_of_freedom diff --git a/docs/reference_guides/model_libraries/generic/costing/process_costing_sslw.rst b/docs/reference_guides/model_libraries/generic/costing/process_costing_sslw.rst index f11992e56f..a604948398 100644 --- a/docs/reference_guides/model_libraries/generic/costing/process_costing_sslw.rst +++ b/docs/reference_guides/model_libraries/generic/costing/process_costing_sslw.rst @@ -851,7 +851,7 @@ note that if plates = `False`, the cost of trays is not included. Base Cost of Platforms and ladders """""""""""""""""""""""""""""""""" -The cost of platforms and ladders is based on the diamter and length in ft. +The cost of platforms and ladders is based on the diameter and length in ft. Horizontal vessels (1: 3 < D < 12 ft): .. math:: self.base\_cost\_platforms\_ladders = 20059*D^{0.20294} diff --git a/docs/reference_guides/model_libraries/generic/property_models/activity_coefficient.rst b/docs/reference_guides/model_libraries/generic/property_models/activity_coefficient.rst index e532d6ed61..9536d6fe29 100644 --- a/docs/reference_guides/model_libraries/generic/property_models/activity_coefficient.rst +++ b/docs/reference_guides/model_libraries/generic/property_models/activity_coefficient.rst @@ -115,7 +115,7 @@ The equilibrium condition, the fugacity of the vapor and liquid phase are define The equilibrium constraint is written as a generic constraint such that it can be extended easily for non-ideal gases and liquids. As this property package only supports an ideal gas, the fugacity coefficient (:math:`\phi_{i}`) for the vapor phase is 1 and hence the expression reduces to :math:`y_{i}P`. For the liquid phase, if the ideal option is selected then the activity coefficient (:math:`\nu_{i}`) is 1. If an activity coefficient model is selected then corresponding constraints are added to compute the activity coefficient. -Typically, the flash calculations are computed at a given temperature, :math:`T`. However, the flash calculations become trivial if the given conditions do not fall in the two phase region. For simulation only studies, the user may know a priori the condition of the stream but when the same set of equations are used for optimization, there is a high probablity that the specifications can transcend the phase envelope and hence the flash equations included may be trivial in the single phase region (i.e. liquid or vapor only). To circumvent this problem, property packages in IDAES that support VLE will compute the flash calculations at an "equilibrium" temperature :math:`T_{eq}`. The equilibrium temperature is computed as follows: +Typically, the flash calculations are computed at a given temperature, :math:`T`. However, the flash calculations become trivial if the given conditions do not fall in the two phase region. For simulation only studies, the user may know a priori the condition of the stream but when the same set of equations are used for optimization, there is a high probability that the specifications can transcend the phase envelope and hence the flash equations included may be trivial in the single phase region (i.e. liquid or vapor only). To circumvent this problem, property packages in IDAES that support VLE will compute the flash calculations at an "equilibrium" temperature :math:`T_{eq}`. The equilibrium temperature is computed as follows: .. math:: T_{1} = max(T_{bubble}, T) .. math:: T_{eq} = min(T_{1}, T_{dew}) diff --git a/docs/reference_guides/model_libraries/generic/unit_models/separator.rst b/docs/reference_guides/model_libraries/generic/unit_models/separator.rst index 32cf8cbc4f..ed9020201a 100644 --- a/docs/reference_guides/model_libraries/generic/unit_models/separator.rst +++ b/docs/reference_guides/model_libraries/generic/unit_models/separator.rst @@ -85,12 +85,16 @@ If `energy_split_basis` is `enthalpy_split`: .. math:: sum_p{h_{in, t, p}*sf_{t, o, p}} = sum_p{h_{t, o, p}} +If `energy_split_basis` is `none`, no energy balance constraint is written. + If `momentum_balance_type` is `pressureTotal`: `pressure_equality_eqn(t, o)`: .. math:: P_{in, t} = P_{t, o} +If `momentum_balance_type` is `none`, no momentum balance constraint is written. + Separators do not support momentum balances using the `pressurePhase`, `momentumTotal` or `momentumPhase` options. Initialization diff --git a/docs/reference_guides/model_libraries/power_generation/unit_models/drum.rst b/docs/reference_guides/model_libraries/power_generation/unit_models/drum.rst index ebb7242500..c97d122771 100644 --- a/docs/reference_guides/model_libraries/power_generation/unit_models/drum.rst +++ b/docs/reference_guides/model_libraries/power_generation/unit_models/drum.rst @@ -62,7 +62,7 @@ Constraints As mentioned above, the drum model imports a `HelmPhaseSeparator` and mixer models, specific documentation for these models can be obtained in: Once the water enters the tank model the main equations calculate water velocity and pressure drop calculation due to gravity based on water level and contraction to downcomer. -Water level (drum_leve) is either fixed for steady state simulation or calculated for dynamic model (Dynamic = True) +Water level (drum_level) is either fixed for steady state simulation or calculated for dynamic model (Dynamic = True) Main assumptions: diff --git a/docs/reference_guides/model_libraries/power_generation/unit_models/drum1D.rst b/docs/reference_guides/model_libraries/power_generation/unit_models/drum1D.rst index 53692d493c..334af46c5b 100644 --- a/docs/reference_guides/model_libraries/power_generation/unit_models/drum1D.rst +++ b/docs/reference_guides/model_libraries/power_generation/unit_models/drum1D.rst @@ -68,7 +68,7 @@ Constraints As mentioned above, the drum model imports a `HelmPhaseSeparator` and mixer models, specific documentation for these models can be obtained in: Once the water enters the tank model the main equations calculate water velocity and pressure drop calculation due to gravity based on water level and contraction to downcomer. -Water level (drum_leve) is either fixed for steady state simulation or calculated for dynamic model (Dynamic = True) +Water level (drum_level) is either fixed for steady state simulation or calculated for dynamic model (Dynamic = True) Main assumptions: diff --git a/docs/static/extra/DiagnosticsFlowchart.png b/docs/static/extra/DiagnosticsFlowchart.png new file mode 100644 index 0000000000..a3e166bb46 Binary files /dev/null and b/docs/static/extra/DiagnosticsFlowchart.png differ diff --git a/idaes/apps/grid_integration/bidder.py b/idaes/apps/grid_integration/bidder.py index 0e2db51032..c1843cc458 100644 --- a/idaes/apps/grid_integration/bidder.py +++ b/idaes/apps/grid_integration/bidder.py @@ -839,7 +839,7 @@ def __init__( forecaster: an initialized LMP forecaster object - fixed_to_schedule: If True, forece market simulator to give the same schedule. + fixed_to_schedule: If True, force market simulator to give the same schedule. Returns: None diff --git a/idaes/apps/uncertainty_propagation/sens.py b/idaes/apps/uncertainty_propagation/sens.py index f3bbde860b..8e46163c18 100644 --- a/idaes/apps/uncertainty_propagation/sens.py +++ b/idaes/apps/uncertainty_propagation/sens.py @@ -33,7 +33,7 @@ check_optimal_termination, ) from pyomo.common.sorting import sorted_robust -from pyomo.core.expr.current import ExpressionReplacementVisitor +from pyomo.core.expr import ExpressionReplacementVisitor from pyomo.common.modeling import unique_component_name from pyomo.opt import SolverFactory, SolverStatus diff --git a/idaes/commands/extensions.py b/idaes/commands/extensions.py index 1a11258854..c2ceecdba4 100644 --- a/idaes/commands/extensions.py +++ b/idaes/commands/extensions.py @@ -198,10 +198,9 @@ def bin_platform(distro): _, platform = idaes.commands.util.download_bin._get_arch_and_platform( fd, distro ) - platform = idaes.commands.util.download_bin._get_release_platform(platform) - click.echo(platform) + click.echo(idaes.commands.util.download_bin._get_release_platform(platform)) except idaes.commands.util.download_bin.UnsupportedPlatformError: - click.echo("No supported binaries found.") + click.echo(f"No supported binaries found for {platform}.") @cb.command(name="extensions-license", help="show license info for binary extensions") diff --git a/idaes/commands/tests/test_commands.py b/idaes/commands/tests/test_commands.py index 790ce17b9b..bcfa681f6e 100644 --- a/idaes/commands/tests/test_commands.py +++ b/idaes/commands/tests/test_commands.py @@ -128,7 +128,7 @@ def test_get_extensions_plat(runner): def test_get_extensions_bad_plat(runner): result = runner.invoke(extensions.bin_platform, ["--distro", "johns_good_linux42"]) assert result.exit_code == 0 - assert result.output == "No supported binaries found.\n" + assert result.output == "No supported binaries found for johns_good_linux42.\n" @pytest.mark.integration diff --git a/idaes/commands/util/download_bin.py b/idaes/commands/util/download_bin.py index e87f25532c..c1a385d3d9 100644 --- a/idaes/commands/util/download_bin.py +++ b/idaes/commands/util/download_bin.py @@ -192,7 +192,7 @@ def _download_package(fd, name, frm, to, platform): raise Exception(f"{name} binaries are unavailable for {platform}") -def _verfiy_checksums(checksum, pname, ptar, ftar): +def _verify_checksums(checksum, pname, ptar, ftar): # If release checksum is not False, nochecksum opt allows hash to be ignored if checksum: for n, p, f in zip(pname, ptar, ftar): @@ -391,7 +391,7 @@ def download_binaries( _download_package(fd, n, frm=u, to=p, platform=platform) # Verify checksums - _verfiy_checksums(checksum, pname, ptar, ftar) + _verify_checksums(checksum, pname, ptar, ftar) # Extract solvers links = {} diff --git a/idaes/config.py b/idaes/config.py index 390199794c..02269e92d5 100644 --- a/idaes/config.py +++ b/idaes/config.py @@ -47,13 +47,17 @@ # Map some platform names to others for get-extensions binary_distro_map = { "macos": "darwin", + "el9": "ubuntu2204", "rhel7": "el7", "rhel8": "el8", + "rhel9": "ubuntu2204", "scientific7": "el7", "centos7": "el7", "centos8": "el8", "rocky8": "el8", + "rocky9": "ubuntu2204", "almalinux8": "el8", + "almalinux9": "ubuntu2204", "debian9": "el7", "debian10": "el8", "debian11": "ubuntu2004", diff --git a/idaes/core/base/control_volume1d.py b/idaes/core/base/control_volume1d.py index 9bfc526e0b..dae0e48ea5 100644 --- a/idaes/core/base/control_volume1d.py +++ b/idaes/core/base/control_volume1d.py @@ -576,7 +576,7 @@ def material_flow_linking_constraints(b, t, x, p, j): self.material_flow_dx = DerivativeVar( self._flow_terms, wrt=self.length_domain, - doc="Parital derivative of material flow " "wrt to normalized length", + doc="Partial derivative of material flow " "wrt to normalized length", units=flow_units, ) diff --git a/idaes/core/base/costing_base.py b/idaes/core/base/costing_base.py index 96b81d6354..1a44c90dd8 100644 --- a/idaes/core/base/costing_base.py +++ b/idaes/core/base/costing_base.py @@ -626,7 +626,7 @@ def initialize(self, *args, **kwargs): Placeholder method for initialization """ # TODO: Implement an initialization method - # TODO: Need to have a general pupose method (block triangularisation?) + # TODO: Need to have a general purpose method (block triangularisation?) # TODO: Should also allow registering custom methods # Vars and Constraints diff --git a/idaes/core/base/property_base.py b/idaes/core/base/property_base.py index 128f22beca..d334658f75 100644 --- a/idaes/core/base/property_base.py +++ b/idaes/core/base/property_base.py @@ -781,7 +781,13 @@ def __getattr__(self, attr): attr: an attribute to create and return. Should be a property component. """ - return build_on_demand(self, attr) + try: + # try the Pyomo Block's __getattr__ method first which will return + # decorators for creating components on the block (e.g. Expression, + # Constraint, ...). + return super().__getattr__(attr) + except AttributeError: + return build_on_demand(self, attr) def calculate_scaling_factors(self): super().calculate_scaling_factors() diff --git a/idaes/core/base/reaction_base.py b/idaes/core/base/reaction_base.py index fc21c47775..981089346f 100644 --- a/idaes/core/base/reaction_base.py +++ b/idaes/core/base/reaction_base.py @@ -361,7 +361,13 @@ def __getattr__(self, attr): attr: an attribute to create and return. Should be a property component. """ - return build_on_demand(self, attr) + try: + # try the Pyomo Block's __getattr__ method first which will return + # decorators for creating components on the block (e.g. Expression, + # Constraint, ...). + return super().__getattr__(attr) + except AttributeError: + return build_on_demand(self, attr) def calculate_scaling_factors(self): super().calculate_scaling_factors() diff --git a/idaes/core/dmf/tests/data_files/pitzer/dataset.json b/idaes/core/dmf/tests/data_files/pitzer/dataset.json index 2454b750f7..d4eec16e0d 100644 --- a/idaes/core/dmf/tests/data_files/pitzer/dataset.json +++ b/idaes/core/dmf/tests/data_files/pitzer/dataset.json @@ -26,7 +26,7 @@ }, { "name": "Standard Cp", - "description": "Standard heat capacities, Debye-Huckel A_J parameter, and virial coeffiCients for the NaCl(aq) heat capacity", + "description": "Standard heat capacities, Debye-Huckel A_J parameter, and virial coefficients for the NaCl(aq) heat capacity", "datafile": "pitzer_4.csv" }, { diff --git a/idaes/core/dmf/tests/test_model_data.py b/idaes/core/dmf/tests/test_model_data.py index 04c84041db..a59e51a6f8 100644 --- a/idaes/core/dmf/tests/test_model_data.py +++ b/idaes/core/dmf/tests/test_model_data.py @@ -209,7 +209,7 @@ def test_unit_conversion(): assert p_pa[0] == pytest.approx(101325, rel=1e-1) assert unit == "kilogram / meter / second ** 2" # AKA Pa - # Test for unit conversion of gauge pressue with different atmosperic + # Test for unit conversion of gauge pressure with different atmosperic # pressure values p, unit = da.unit_convert( p_psi, "psig", "atm", ambient_pressure=np.array([1, 1.1, 1.2]) diff --git a/idaes/core/solvers/petsc.py b/idaes/core/solvers/petsc.py index 8e108119b9..7a29f5a958 100644 --- a/idaes/core/solvers/petsc.py +++ b/idaes/core/solvers/petsc.py @@ -420,6 +420,7 @@ def petsc_dae_by_time_element( initial_variables=None, detect_initial=True, skip_initial=False, + snes_solver="petsc_snes", snes_options=None, ts_options=None, keepfiles=False, @@ -453,7 +454,9 @@ def petsc_dae_by_time_element( calculated. This can be useful, for example, if you read initial conditions from a separately solved steady state problem, or otherwise know the initial conditions. - snes_options (dict): PETSc nonlinear equation solver options + snes_solver (str): default=petsc_snes, the nonlinear equations solver + to use for the initial conditions (e.g. petsc_snes, ipopt, ...). + snes_options (dict): nonlinear equation solver options ts_options (dict): PETSc time-stepping solver options keepfiles (bool): pass to keepfiles arg for solvers symbolic_solver_labels (bool): pass to symbolic_solver_labels argument @@ -517,7 +520,7 @@ def petsc_dae_by_time_element( if not skip_initial: # Nonlinear equation solver for initial conditions - solver_snes = pyo.SolverFactory("petsc_snes", options=snes_options) + solver_snes = pyo.SolverFactory(snes_solver, options=snes_options) # list of constraints to add to the initial condition problem if initial_constraints is None: initial_constraints = [] diff --git a/idaes/core/surrogate/alamopy.py b/idaes/core/surrogate/alamopy.py index ebed33d786..78106a8109 100644 --- a/idaes/core/surrogate/alamopy.py +++ b/idaes/core/surrogate/alamopy.py @@ -86,8 +86,8 @@ class Screener(Enum): "sinfcns", "cosfcns", "constant", - "grbfcns", - "rbfparam", + # "grbfcns", # TODO: deprecated + # "rbfparam", # TODO: deprecated "modeler", "builder", "backstepper", @@ -282,7 +282,7 @@ class AlamoTrainer(SurrogateTrainer): description="Include cosine basis functions if True.", ), ) - CONFIG.declare( + CONFIG.declare( # TODO: deprecated option "grbfcns", ConfigValue( default=None, @@ -290,7 +290,7 @@ class AlamoTrainer(SurrogateTrainer): description="Include Gaussian radial basis functions if True.", ), ) - CONFIG.declare( + CONFIG.declare( # TODO: deprecated option "rbfparam", ConfigValue( default=None, @@ -669,6 +669,15 @@ class AlamoTrainer(SurrogateTrainer): def __init__(self, **settings): super().__init__(**settings) + # TODO: Deprecation warnings for RBF options + if self.config.grbfcns is not None or self.config.rbfparam is not None: + raise ConfigurationError( + "Support for radial basis functions in the ALAMOpy wrapper have been " + "deprecated due to limitations in the current implementation. " + "Pull requests are welcome to address this, otherwise we recommend using " + "Pysmo which includes support for radial basis functions." + ) + self._temp_context = None self._almfile = None self._trcfile = None diff --git a/idaes/core/surrogate/pysmo/kriging.py b/idaes/core/surrogate/pysmo/kriging.py index fce8c719fb..c936cf97a3 100644 --- a/idaes/core/surrogate/pysmo/kriging.py +++ b/idaes/core/surrogate/pysmo/kriging.py @@ -401,7 +401,9 @@ def parameter_optimization(self, p): initial_value_list = initial_value_list.tolist() initial_value_list.append(1e-4) initial_value = np.array(initial_value_list) - initial_value = initial_value.reshape(initial_value.shape[0], 1) + initial_value = initial_value.reshape( + initial_value.shape[0], + ) # Create bounds for variables. All logthetas btw (-4, 4), reg param between (1e-9, 0.1) bounds = [] for i in range(0, len(initial_value_list)): diff --git a/idaes/core/surrogate/tests/test_alamopy.py b/idaes/core/surrogate/tests/test_alamopy.py index 92c1246938..6cc6d3ebd2 100644 --- a/idaes/core/surrogate/tests/test_alamopy.py +++ b/idaes/core/surrogate/tests/test_alamopy.py @@ -71,6 +71,45 @@ def alamo_trainer(self): training_dataframe=data, ) + @pytest.mark.unit + def test_rbf_deprecation(self): + data = {"x1": [1, 2, 3, 4], "x2": [5, 6, 7, 8], "z1": [10, 20, 30, 40]} + data = pd.DataFrame(data) + + input_labels = ["x1", "x2"] + output_labels = ["z1"] + bnds = {"x1": (0, 5), "x2": (0, 10)} + + with pytest.raises( + ConfigurationError, + match="Support for radial basis functions in the ALAMOpy wrapper have been " + "deprecated due to limitations in the current implementation. " + "Pull requests are welcome to address this, otherwise we recommend using " + "Pysmo which includes support for radial basis functions.", + ): + AlamoTrainer( + input_labels=input_labels, + output_labels=output_labels, + input_bounds=bnds, + training_dataframe=data, + grbfcns=True, + ) + + with pytest.raises( + ConfigurationError, + match="Support for radial basis functions in the ALAMOpy wrapper have been " + "deprecated due to limitations in the current implementation. " + "Pull requests are welcome to address this, otherwise we recommend using " + "Pysmo which includes support for radial basis functions.", + ): + AlamoTrainer( + input_labels=input_labels, + output_labels=output_labels, + input_bounds=bnds, + training_dataframe=data, + rbfparam=1.0, + ) + @pytest.mark.unit def test_get_files(self, alamo_trainer): alamo_trainer.config.filename = "foo.alm" @@ -486,8 +525,6 @@ def test_writer_full_config(self, alamo_trainer): "sinfcns 1\n" "cosfcns 0\n" "constant 1\n" - "grbfcns 1\n" - "rbfparam 7.0\n" "modeler 3\n" "builder 1\n" "backstepper 0\n" diff --git a/idaes/core/util/model_statistics.py b/idaes/core/util/model_statistics.py index 32b1bd5ad6..c0ac0b1d5c 100644 --- a/idaes/core/util/model_statistics.py +++ b/idaes/core/util/model_statistics.py @@ -22,7 +22,7 @@ from pyomo.environ import Block, Constraint, Expression, Objective, Var, value from pyomo.dae import DerivativeVar -from pyomo.core.expr.current import identify_variables +from pyomo.core.expr import identify_variables from pyomo.common.collections import ComponentSet diff --git a/idaes/models/properties/modular_properties/phase_equil/bubble_dew.py b/idaes/models/properties/modular_properties/phase_equil/bubble_dew.py index 2c39939135..4bd953e686 100644 --- a/idaes/models/properties/modular_properties/phase_equil/bubble_dew.py +++ b/idaes/models/properties/modular_properties/phase_equil/bubble_dew.py @@ -26,6 +26,7 @@ get_component_object as cobj, ) import idaes.core.util.scaling as iscale +from idaes.core.util.exceptions import ConfigurationError # _valid_VL_component_list return variables that are not need in all cases # pylint: disable=W0612 @@ -42,6 +43,7 @@ class IdealBubbleDew: # calculate concentrations at the bubble and dew points @staticmethod def temperature_bubble(b): + _non_vle_phase_check(b) try: def rule_bubble_temp(b, p1, p2): @@ -161,6 +163,7 @@ def scale_temperature_bubble(b, overwrite=True): # Dew temperature methods @staticmethod def temperature_dew(b): + _non_vle_phase_check(b) try: def rule_dew_temp(b, p1, p2): @@ -282,6 +285,7 @@ def scale_temperature_dew(b, overwrite=True): # Bubble pressure methods @staticmethod def pressure_bubble(b): + _non_vle_phase_check(b) try: def rule_bubble_press(b, p1, p2): @@ -386,6 +390,7 @@ def scale_pressure_bubble(b, overwrite=True): # Dew pressure methods @staticmethod def pressure_dew(b): + _non_vle_phase_check(b) try: def rule_dew_press(b, p1, p2): @@ -887,3 +892,11 @@ def _valid_VL_component_list(blk, pp): v_only_comps.append(j) return l_phase, v_phase, vl_comps, henry_comps, l_only_comps, v_only_comps + + +def _non_vle_phase_check(blk): + if len(blk.phase_list) > 2: + raise ConfigurationError( + "Ideal assumption for calculating bubble and/or dew points is only valid " + "for systems with two phases. Please use LogBubbleDew approach instead." + ) diff --git a/idaes/models/properties/modular_properties/phase_equil/tests/test_bubble_dew.py b/idaes/models/properties/modular_properties/phase_equil/tests/test_bubble_dew.py index 28f34065f0..40c2f7e0e5 100644 --- a/idaes/models/properties/modular_properties/phase_equil/tests/test_bubble_dew.py +++ b/idaes/models/properties/modular_properties/phase_equil/tests/test_bubble_dew.py @@ -29,6 +29,7 @@ GenericParameterData, ) from idaes.models.properties.modular_properties.base.tests.dummy_eos import DummyEoS +from idaes.core.util.exceptions import ConfigurationError # Dummy class to use for Psat calls @@ -142,6 +143,43 @@ def test_expressions(self, frame): frame.props[1].eq_mole_frac_tbub[pp[0], pp[1], k].body ) == pytest.approx(0, abs=1e-8) + @pytest.mark.unit + def test_inert_phases(self): + m = ConcreteModel() + + # Dummy params block + m.params = DummyParameterBlock( + components={ + "H2O": {"pressure_sat_comp": pressure_sat_comp}, + "EtOH": {"pressure_sat_comp": pressure_sat_comp}, + }, + phases={ + "Liq": {"equation_of_state": DummyEoS}, + "Vap": {"equation_of_state": DummyEoS}, + "Sold": {"equation_of_state": DummyEoS}, + }, + state_definition=modules[__name__], + pressure_ref=100000.0, + temperature_ref=300, + base_units={ + "time": pyunits.s, + "length": pyunits.m, + "mass": pyunits.kg, + "amount": pyunits.mol, + "temperature": pyunits.K, + }, + ) + m.params._pe_pairs = Set(initialize=[("Vap", "Liq")]) + + m.props = m.params.build_state_block([1], defined_state=False) + + with pytest.raises( + ConfigurationError, + match="Ideal assumption for calculating bubble and/or dew points is only valid " + "for systems with two phases. Please use LogBubbleDew approach instead.", + ): + IdealBubbleDew.temperature_bubble(m.props[1]) + class TestDewTempIdeal(object): @pytest.mark.unit @@ -191,6 +229,43 @@ def test_expressions(self, frame): frame.props[1].eq_mole_frac_tdew[pp[0], pp[1], k].body ) == pytest.approx(0, abs=1e-8) + @pytest.mark.unit + def test_inert_phases(self): + m = ConcreteModel() + + # Dummy params block + m.params = DummyParameterBlock( + components={ + "H2O": {"pressure_sat_comp": pressure_sat_comp}, + "EtOH": {"pressure_sat_comp": pressure_sat_comp}, + }, + phases={ + "Liq": {"equation_of_state": DummyEoS}, + "Vap": {"equation_of_state": DummyEoS}, + "Sold": {"equation_of_state": DummyEoS}, + }, + state_definition=modules[__name__], + pressure_ref=100000.0, + temperature_ref=300, + base_units={ + "time": pyunits.s, + "length": pyunits.m, + "mass": pyunits.kg, + "amount": pyunits.mol, + "temperature": pyunits.K, + }, + ) + m.params._pe_pairs = Set(initialize=[("Vap", "Liq")]) + + m.props = m.params.build_state_block([1], defined_state=False) + + with pytest.raises( + ConfigurationError, + match="Ideal assumption for calculating bubble and/or dew points is only valid " + "for systems with two phases. Please use LogBubbleDew approach instead.", + ): + IdealBubbleDew.temperature_bubble(m.props[1]) + class TestBubblePresIdeal(object): @pytest.mark.unit @@ -239,6 +314,43 @@ def test_expressions(self, frame): frame.props[1].eq_mole_frac_pbub[pp[0], pp[1], k].body ) == pytest.approx(0, abs=1e-8) + @pytest.mark.unit + def test_inert_phases(self): + m = ConcreteModel() + + # Dummy params block + m.params = DummyParameterBlock( + components={ + "H2O": {"pressure_sat_comp": pressure_sat_comp}, + "EtOH": {"pressure_sat_comp": pressure_sat_comp}, + }, + phases={ + "Liq": {"equation_of_state": DummyEoS}, + "Vap": {"equation_of_state": DummyEoS}, + "Sold": {"equation_of_state": DummyEoS}, + }, + state_definition=modules[__name__], + pressure_ref=100000.0, + temperature_ref=300, + base_units={ + "time": pyunits.s, + "length": pyunits.m, + "mass": pyunits.kg, + "amount": pyunits.mol, + "temperature": pyunits.K, + }, + ) + m.params._pe_pairs = Set(initialize=[("Vap", "Liq")]) + + m.props = m.params.build_state_block([1], defined_state=False) + + with pytest.raises( + ConfigurationError, + match="Ideal assumption for calculating bubble and/or dew points is only valid " + "for systems with two phases. Please use LogBubbleDew approach instead.", + ): + IdealBubbleDew.temperature_bubble(m.props[1]) + class TestDewPressureIdeal(object): @pytest.mark.unit @@ -287,3 +399,40 @@ def test_expressions(self, frame): assert value( frame.props[1].eq_mole_frac_pdew[pp[0], pp[1], k].body ) == pytest.approx(0, abs=1e-8) + + @pytest.mark.unit + def test_inert_phases(self): + m = ConcreteModel() + + # Dummy params block + m.params = DummyParameterBlock( + components={ + "H2O": {"pressure_sat_comp": pressure_sat_comp}, + "EtOH": {"pressure_sat_comp": pressure_sat_comp}, + }, + phases={ + "Liq": {"equation_of_state": DummyEoS}, + "Vap": {"equation_of_state": DummyEoS}, + "Sold": {"equation_of_state": DummyEoS}, + }, + state_definition=modules[__name__], + pressure_ref=100000.0, + temperature_ref=300, + base_units={ + "time": pyunits.s, + "length": pyunits.m, + "mass": pyunits.kg, + "amount": pyunits.mol, + "temperature": pyunits.K, + }, + ) + m.params._pe_pairs = Set(initialize=[("Vap", "Liq")]) + + m.props = m.params.build_state_block([1], defined_state=False) + + with pytest.raises( + ConfigurationError, + match="Ideal assumption for calculating bubble and/or dew points is only valid " + "for systems with two phases. Please use LogBubbleDew approach instead.", + ): + IdealBubbleDew.temperature_bubble(m.props[1]) diff --git a/idaes/models/properties/modular_properties/reactions/equilibrium_forms.py b/idaes/models/properties/modular_properties/reactions/equilibrium_forms.py index e9b10def23..eeada80ed7 100644 --- a/idaes/models/properties/modular_properties/reactions/equilibrium_forms.py +++ b/idaes/models/properties/modular_properties/reactions/equilibrium_forms.py @@ -19,7 +19,7 @@ # TODO: Look into protected access issues # pylint: disable=protected-access -from pyomo.environ import Param, units as pyunits +from pyomo.environ import Param, units as pyunits, value from idaes.core.util.math import smooth_max from idaes.core.util.exceptions import ConfigurationError @@ -96,7 +96,7 @@ def calculate_scaling_factors(b, sf_keq): # ---------------------------------------------------------------------------- class solubility_product: """ - Complementariity formulation for solid precipitation + Complementarity formulation for solid precipitation Thanks to Larry Biegler for the formulation Like any phase equilibrium, solid precipitation is complicated by @@ -113,7 +113,7 @@ class solubility_product: Thus, only one of S and Q can be greater than zero at any time. This can be written in the form of a complementarity constraint as: - * S - MAX(0, S-Q) == 0 + * Q - MAX(0, Q-S) == 0 where S is assumed to be the sum of the flowrates any solids formed in the reaction. This allows for multiple solid products, and only applies the @@ -131,6 +131,20 @@ def build_parameters(rblock, config): mutable=True, initialize=1e-4, doc="Smoothing parameter for smooth maximum" ) + rblock.s_norm = Param( + mutable=True, + initialize=1e-4, + doc="Normalizing factor for solid precipitation term", + ) + if hasattr(rblock, "k_eq_ref"): + rblock.s_norm.set_value(value(rblock.k_eq_ref)) + + rblock.s_scale = Param( + mutable=True, + initialize=1, + doc="Scaling factor for solid precipitation term w.r.t saturated status Q = Ksp - f(C)", + ) + @staticmethod def return_expression(b, rblock, r_idx, T): e = None @@ -181,6 +195,8 @@ def return_expression(b, rblock, r_idx, T): if sunits is not None: s = s / sunits + s = rblock.s_scale * s / (s + rblock.s_norm) + Q = b.k_eq[r_idx] - e # Need to remove units again @@ -189,7 +205,7 @@ def return_expression(b, rblock, r_idx, T): if Qunits is not None: Q = Q / Qunits - return s - smooth_max(0, s - Q, rblock.eps) == 0 + return Q - smooth_max(0, Q - s, rblock.eps) == 0 @staticmethod def calculate_scaling_factors(b, sf_keq): @@ -198,7 +214,7 @@ def calculate_scaling_factors(b, sf_keq): class log_solubility_product: """ - Complementariity formulation for solid precipitation + Complementarity formulation for solid precipitation Thanks to Larry Biegler for the formulation Like any phase equilibrium, solid precipitation is complicated by @@ -216,7 +232,7 @@ class uses a log form to represent to equilibrium constraint. Thus, only one of S and Q can be greater than zero at any time. This can be written in the form of a complementarity constraint as: - * S - MAX(0, S-Q) == 0 + * Q - MAX(0, Q-S) == 0 where S is assumed to be the sum of the flowrates any solids formed in the reaction. This allows for multiple solid products, and only applies the @@ -234,6 +250,20 @@ def build_parameters(rblock, config): mutable=True, initialize=1e-4, doc="Smoothing parameter for smooth maximum" ) + rblock.s_norm = Param( + mutable=True, + initialize=1e-4, + doc="Normalizing factor for solid precipitation term", + ) + if hasattr(rblock, "k_eq_ref"): + rblock.s_norm.set_value(value(rblock.k_eq_ref)) + + rblock.s_scale = Param( + mutable=True, + initialize=10, + doc="Scaling factor for solid precipitation term w.r.t saturated status Q = ln(Ksp) - ln(f(C))", + ) + @staticmethod def return_expression(b, rblock, r_idx, T): e = None @@ -284,10 +314,12 @@ def return_expression(b, rblock, r_idx, T): if sunits is not None: s = s / sunits + s = rblock.s_scale * s / (s + rblock.s_norm) + Q = b.log_k_eq[r_idx] - e # Q should be unitless due to log form - return s - smooth_max(0, s - Q, rblock.eps) == 0 + return Q - smooth_max(0, Q - s, rblock.eps) == 0 @staticmethod def calculate_scaling_factors(b, sf_keq): diff --git a/idaes/models/properties/modular_properties/reactions/tests/test_equilibrium_forms.py b/idaes/models/properties/modular_properties/reactions/tests/test_equilibrium_forms.py index 3dab4f344c..e0fe4a7db0 100644 --- a/idaes/models/properties/modular_properties/reactions/tests/test_equilibrium_forms.py +++ b/idaes/models/properties/modular_properties/reactions/tests/test_equilibrium_forms.py @@ -414,6 +414,10 @@ def test_solubility_no_order(): # Check parameter construction assert isinstance(m.rparams.reaction_r1.eps, Param) assert value(m.rparams.reaction_r1.eps) == 1e-4 + assert isinstance(m.rparams.reaction_r1.s_norm, Param) + assert value(m.rparams.reaction_r1.s_norm) == 1e-4 + assert isinstance(m.rparams.reaction_r1.s_norm, Param) + assert value(m.rparams.reaction_r1.s_scale) == 1 assert isinstance(m.rparams.reaction_r1.reaction_order, Var) assert len(m.rparams.reaction_r1.reaction_order) == 6 @@ -434,16 +438,19 @@ def test_solubility_no_order(): m.thermo[1].flow_mol_phase_comp["sol", "c1"] + m.thermo[1].flow_mol_phase_comp["sol", "c2"] ) / (pyunits.mol / pyunits.s) - Q = m.rxn[1].k_eq["r1"] - ( - m.thermo[1].mole_frac_phase_comp["p1", "c1"] - ** m.rparams.reaction_r1.reaction_order["p1", "c1"] - * m.thermo[1].mole_frac_phase_comp["p1", "c2"] - ** m.rparams.reaction_r1.reaction_order["p1", "c2"] - ) + s = m.rparams.reaction_r1.s_scale * s / (s + m.rparams.reaction_r1.s_norm) - assert str(rform) == str( - s - smooth_max(0, s - Q / pyunits.dimensionless, m.rparams.reaction_r1.eps) == 0 - ) + Q = ( + m.rxn[1].k_eq["r1"] + - ( + m.thermo[1].mole_frac_phase_comp["p1", "c1"] + ** m.rparams.reaction_r1.reaction_order["p1", "c1"] + * m.thermo[1].mole_frac_phase_comp["p1", "c2"] + ** m.rparams.reaction_r1.reaction_order["p1", "c2"] + ) + ) / (pyunits.dimensionless) + + assert str(rform) == str(Q - smooth_max(0, Q - s, m.rparams.reaction_r1.eps) == 0) @pytest.mark.unit @@ -505,6 +512,10 @@ def test_solubility_product_with_order(): # Check parameter construction assert isinstance(m.rparams.reaction_r1.eps, Param) assert value(m.rparams.reaction_r1.eps) == 1e-4 + assert isinstance(m.rparams.reaction_r1.s_norm, Param) + assert value(m.rparams.reaction_r1.s_norm) == 1e-4 + assert isinstance(m.rparams.reaction_r1.s_norm, Param) + assert value(m.rparams.reaction_r1.s_scale) == 1 assert isinstance(m.rparams.reaction_r1.reaction_order, Var) assert len(m.rparams.reaction_r1.reaction_order) == 6 assert m.rparams.reaction_r1.reaction_order["p1", "c1"].value == 1 @@ -523,24 +534,27 @@ def test_solubility_product_with_order(): m.thermo[1].flow_mol_phase_comp["sol", "c1"] + m.thermo[1].flow_mol_phase_comp["sol", "c2"] ) / (pyunits.mol / pyunits.s) - Q = m.rxn[1].k_eq["r1"] - ( - m.thermo[1].mole_frac_phase_comp["p1", "c1"] - ** m.rparams.reaction_r1.reaction_order["p1", "c1"] - * m.thermo[1].mole_frac_phase_comp["p1", "c2"] - ** m.rparams.reaction_r1.reaction_order["p1", "c2"] - * m.thermo[1].mole_frac_phase_comp["p2", "c1"] - ** m.rparams.reaction_r1.reaction_order["p2", "c1"] - * m.thermo[1].mole_frac_phase_comp["p2", "c2"] - ** m.rparams.reaction_r1.reaction_order["p2", "c2"] - * m.thermo[1].mole_frac_phase_comp["sol", "c1"] - ** m.rparams.reaction_r1.reaction_order["sol", "c1"] - * m.thermo[1].mole_frac_phase_comp["sol", "c2"] - ** m.rparams.reaction_r1.reaction_order["sol", "c2"] - ) + s = m.rparams.reaction_r1.s_scale * s / (s + m.rparams.reaction_r1.s_norm) - assert str(rform) == str( - s - smooth_max(0, s - Q / pyunits.dimensionless, m.rparams.reaction_r1.eps) == 0 - ) + Q = ( + m.rxn[1].k_eq["r1"] + - ( + m.thermo[1].mole_frac_phase_comp["p1", "c1"] + ** m.rparams.reaction_r1.reaction_order["p1", "c1"] + * m.thermo[1].mole_frac_phase_comp["p1", "c2"] + ** m.rparams.reaction_r1.reaction_order["p1", "c2"] + * m.thermo[1].mole_frac_phase_comp["p2", "c1"] + ** m.rparams.reaction_r1.reaction_order["p2", "c1"] + * m.thermo[1].mole_frac_phase_comp["p2", "c2"] + ** m.rparams.reaction_r1.reaction_order["p2", "c2"] + * m.thermo[1].mole_frac_phase_comp["sol", "c1"] + ** m.rparams.reaction_r1.reaction_order["sol", "c1"] + * m.thermo[1].mole_frac_phase_comp["sol", "c2"] + ** m.rparams.reaction_r1.reaction_order["sol", "c2"] + ) + ) / (pyunits.dimensionless) + + assert str(rform) == str(Q - smooth_max(0, Q - s, m.rparams.reaction_r1.eps) == 0) @pytest.mark.unit @@ -647,6 +661,10 @@ def test_log_solubility_no_order(): # Check parameter construction assert isinstance(m.rparams.reaction_r1.eps, Param) assert value(m.rparams.reaction_r1.eps) == 1e-4 + assert isinstance(m.rparams.reaction_r1.s_norm, Param) + assert value(m.rparams.reaction_r1.s_norm) == 1e-4 + assert isinstance(m.rparams.reaction_r1.s_norm, Param) + assert value(m.rparams.reaction_r1.s_scale) == 10 assert isinstance(m.rparams.reaction_r1.reaction_order, Var) assert len(m.rparams.reaction_r1.reaction_order) == 6 @@ -667,6 +685,8 @@ def test_log_solubility_no_order(): m.thermo[1].flow_mol_phase_comp["sol", "c1"] + m.thermo[1].flow_mol_phase_comp["sol", "c2"] ) / (pyunits.mol / pyunits.s) + s = m.rparams.reaction_r1.s_scale * s / (s + m.rparams.reaction_r1.s_norm) + Q = m.rxn[1].log_k_eq["r1"] - ( m.thermo[1].log_mole_frac_phase_comp["p1", "c1"] * m.rparams.reaction_r1.reaction_order["p1", "c1"] @@ -674,7 +694,7 @@ def test_log_solubility_no_order(): * m.rparams.reaction_r1.reaction_order["p1", "c2"] ) - assert str(rform) == str(s - smooth_max(0, s - Q, m.rparams.reaction_r1.eps) == 0) + assert str(rform) == str(Q - smooth_max(0, Q - s, m.rparams.reaction_r1.eps) == 0) @pytest.mark.unit @@ -739,6 +759,10 @@ def test_log_solubility_product_with_order(): # Check parameter construction assert isinstance(m.rparams.reaction_r1.eps, Param) assert value(m.rparams.reaction_r1.eps) == 1e-4 + assert isinstance(m.rparams.reaction_r1.s_norm, Param) + assert value(m.rparams.reaction_r1.s_norm) == 1e-4 + assert isinstance(m.rparams.reaction_r1.s_norm, Param) + assert value(m.rparams.reaction_r1.s_scale) == 10 assert isinstance(m.rparams.reaction_r1.reaction_order, Var) assert len(m.rparams.reaction_r1.reaction_order) == 6 assert m.rparams.reaction_r1.reaction_order["p1", "c1"].value == 1 @@ -757,6 +781,8 @@ def test_log_solubility_product_with_order(): m.thermo[1].flow_mol_phase_comp["sol", "c1"] + m.thermo[1].flow_mol_phase_comp["sol", "c2"] ) / (pyunits.mol / pyunits.s) + s = m.rparams.reaction_r1.s_scale * s / (s + m.rparams.reaction_r1.s_norm) + Q = m.rxn[1].log_k_eq["r1"] - ( m.thermo[1].log_mole_frac_phase_comp["p1", "c1"] * m.rparams.reaction_r1.reaction_order["p1", "c1"] @@ -772,7 +798,7 @@ def test_log_solubility_product_with_order(): * m.rparams.reaction_r1.reaction_order["sol", "c2"] ) - assert str(rform) == str(s - smooth_max(0, s - Q, m.rparams.reaction_r1.eps) == 0) + assert str(rform) == str(Q - smooth_max(0, Q - s, m.rparams.reaction_r1.eps) == 0) @pytest.mark.unit diff --git a/idaes/models/properties/modular_properties/reactions/tests/test_solubility_product_verification.py b/idaes/models/properties/modular_properties/reactions/tests/test_solubility_product_verification.py index 7b778ff295..cebde493e8 100644 --- a/idaes/models/properties/modular_properties/reactions/tests/test_solubility_product_verification.py +++ b/idaes/models/properties/modular_properties/reactions/tests/test_solubility_product_verification.py @@ -40,6 +40,7 @@ SolidPhase, ) from idaes.core.solvers import get_solver +import idaes.core.util.scaling as iscale from idaes.models.properties.modular_properties.state_definitions import FpcTP from idaes.models.properties.modular_properties.eos.ideal import Ideal @@ -140,6 +141,7 @@ def model(self): m.rxn = m.rparams.build_reaction_block( [0], state_block=m.state, has_equilibrium=True ) + m.rxn[0].eps = 1e-14 return m @@ -191,9 +193,6 @@ def test_subsaturated(self, model): # For subsaturated systems, fix the Na and Cl flows and check that no # solid is formed. model.state[0].flow_mol_phase_comp["Liq", "H2O"].fix(55.56) - model.state[0].flow_mol_phase_comp["Liq", "Na+"].fix(0) - model.state[0].flow_mol_phase_comp["Liq", "Cl-"].fix(0) - model.state[0].flow_mol_phase_comp["Sol", "NaCl"].set_value(0) for i in range(11): for j in range(11): @@ -204,8 +203,19 @@ def test_subsaturated(self, model): if j == 0: j = 1e-8 + # reinitialize after each iteration + model.state[0].mole_frac_phase_comp["Liq", "H2O"].set_value(0.25) + model.state[0].mole_frac_phase_comp["Sol", "NaCl"].set_value(0.25) + model.state[0].mole_frac_phase_comp["Liq", "Na+"].set_value(0.25) + model.state[0].mole_frac_phase_comp["Liq", "Cl-"].set_value(0.25) + model.state[0].flow_mol_phase_comp["Liq", "Na+"].fix(i) model.state[0].flow_mol_phase_comp["Liq", "Cl-"].fix(j) + model.state[0].flow_mol_phase_comp["Sol", "NaCl"].set_value(0) + + iscale.constraint_scaling_transform( + model.rxn[0].equilibrium_constraint["S1"], 10 + ) results = solver.solve(model) @@ -238,6 +248,7 @@ def model(self): energy_balance_type=EnergyBalanceType.none, ) + m.fs.rparams.reaction_S1.eps = 1e-14 m.fs.R101.inlet.flow_mol_phase_comp[0, "Liq", "H2O"].fix(55.56) m.fs.R101.inlet.flow_mol_phase_comp[0, "Liq", "Na+"].fix(1e-8) m.fs.R101.inlet.flow_mol_phase_comp[0, "Liq", "Cl-"].fix(1e-8) @@ -257,6 +268,12 @@ def test_subsaturated(self, model): i == 1e-8 model.fs.R101.inlet.flow_mol_phase_comp[0, "Sol", "NaCl"].fix(i) + iscale.constraint_scaling_transform( + model.fs.R101.control_volume.reactions[0.0].equilibrium_constraint[ + "S1" + ], + 10, + ) results = solver.solve(model) @@ -283,10 +300,10 @@ def test_saturated(self, model): assert pytest.approx(0, abs=1.1e-3) == value( model.fs.R101.outlet.flow_mol_phase_comp[0, "Sol", "NaCl"] ) - assert pytest.approx(6.158968, rel=1e-5) == value( + assert pytest.approx(6.159876, rel=1e-5) == value( model.fs.R101.outlet.flow_mol_phase_comp[0, "Liq", "Na+"] ) - assert pytest.approx(6.158968, rel=1e-5) == value( + assert pytest.approx(6.159876, rel=1e-5) == value( model.fs.R101.outlet.flow_mol_phase_comp[0, "Liq", "Cl-"] ) diff --git a/idaes/models/properties/modular_properties/state_definitions/FPhx.py b/idaes/models/properties/modular_properties/state_definitions/FPhx.py index 58966f94ad..1365664076 100644 --- a/idaes/models/properties/modular_properties/state_definitions/FPhx.py +++ b/idaes/models/properties/modular_properties/state_definitions/FPhx.py @@ -20,6 +20,7 @@ # TODO: Look into protected access issues # pylint: disable=protected-access +from types import MethodType from pyomo.environ import ( Constraint, Expression, @@ -277,13 +278,13 @@ def rule_phase_frac(b, p): # ------------------------------------------------------------------------- # General Methods - def get_material_flow_terms_FTPx(p, j): + def get_material_flow_terms_FTPx(b, p, j): """Create material flow terms for control volume.""" return b.flow_mol_phase_comp[p, j] - b.get_material_flow_terms = get_material_flow_terms_FTPx + b.get_material_flow_terms = MethodType(get_material_flow_terms_FTPx, b) - def get_enthalpy_flow_terms_FTPx(p): + def get_enthalpy_flow_terms_FTPx(b, p): """Create enthalpy flow terms.""" # enth_mol_phase probably does not exist when this is created # Use try/except to build flow term if not present @@ -297,9 +298,9 @@ def rule_eflow(b, p): eflow = b._enthalpy_flow_term = Expression(b.phase_list, rule=rule_eflow) return eflow[p] - b.get_enthalpy_flow_terms = get_enthalpy_flow_terms_FTPx + b.get_enthalpy_flow_terms = MethodType(get_enthalpy_flow_terms_FTPx, b) - def get_material_density_terms_FTPx(p, j): + def get_material_density_terms_FTPx(b, p, j): """Create material density terms.""" # dens_mol_phase probably does not exist when this is created # Use try/except to build term if not present @@ -315,9 +316,9 @@ def rule_mdens(b, p, j): ) return mdens[p, j] - b.get_material_density_terms = get_material_density_terms_FTPx + b.get_material_density_terms = MethodType(get_material_density_terms_FTPx, b) - def get_energy_density_terms_FTPx(p): + def get_energy_density_terms_FTPx(b, p): """Create energy density terms.""" # Density and energy terms probably do not exist when this is created # Use try/except to build term if not present @@ -331,7 +332,7 @@ def rule_edens(b, p): edens = b._energy_density_term = Expression(b.phase_list, rule=rule_edens) return edens[p] - b.get_energy_density_terms = get_energy_density_terms_FTPx + b.get_energy_density_terms = MethodType(get_energy_density_terms_FTPx, b) def default_material_balance_type_FTPx(): return MaterialBalanceType.componentTotal @@ -348,7 +349,7 @@ def get_material_flow_basis_FTPx(): b.get_material_flow_basis = get_material_flow_basis_FTPx - def define_state_vars_FPhx(): + def define_state_vars_FPhx(b): """Define state vars.""" return { "flow_mol": b.flow_mol, @@ -357,9 +358,9 @@ def define_state_vars_FPhx(): "pressure": b.pressure, } - b.define_state_vars = define_state_vars_FPhx + b.define_state_vars = MethodType(define_state_vars_FPhx, b) - def define_display_vars_FPhx(): + def define_display_vars_FPhx(b): """Define display vars.""" return { "Total Molar Flowrate": b.flow_mol, @@ -368,7 +369,7 @@ def define_display_vars_FPhx(): "Pressure": b.pressure, } - b.define_display_vars = define_display_vars_FPhx + b.define_display_vars = MethodType(define_display_vars_FPhx, b) def define_default_scaling_factors(b): diff --git a/idaes/models/properties/modular_properties/state_definitions/FTPx.py b/idaes/models/properties/modular_properties/state_definitions/FTPx.py index 77a8c8318f..b89b9583af 100644 --- a/idaes/models/properties/modular_properties/state_definitions/FTPx.py +++ b/idaes/models/properties/modular_properties/state_definitions/FTPx.py @@ -20,6 +20,7 @@ # TODO: Look into protected access issues # pylint: disable=protected-access +from types import MethodType from pyomo.environ import ( Constraint, Expression, @@ -264,13 +265,13 @@ def rule_phase_frac(b, p): # ------------------------------------------------------------------------- # General Methods - def get_material_flow_terms_FTPx(p, j): + def get_material_flow_terms_FTPx(b, p, j): """Create material flow terms for control volume.""" return b.flow_mol_phase_comp[p, j] - b.get_material_flow_terms = get_material_flow_terms_FTPx + b.get_material_flow_terms = MethodType(get_material_flow_terms_FTPx, b) - def get_enthalpy_flow_terms_FTPx(p): + def get_enthalpy_flow_terms_FTPx(b, p): """Create enthalpy flow terms.""" # enth_mol_phase probably does not exist when this is created # Use try/except to build flow term if not present @@ -284,9 +285,9 @@ def rule_eflow(b, p): eflow = b._enthalpy_flow_term = Expression(b.phase_list, rule=rule_eflow) return eflow[p] - b.get_enthalpy_flow_terms = get_enthalpy_flow_terms_FTPx + b.get_enthalpy_flow_terms = MethodType(get_enthalpy_flow_terms_FTPx, b) - def get_material_density_terms_FTPx(p, j): + def get_material_density_terms_FTPx(b, p, j): """Create material density terms.""" # dens_mol_phase probably does not exist when this is created # Use try/except to build term if not present @@ -302,9 +303,9 @@ def rule_mdens(b, p, j): ) return mdens[p, j] - b.get_material_density_terms = get_material_density_terms_FTPx + b.get_material_density_terms = MethodType(get_material_density_terms_FTPx, b) - def get_energy_density_terms_FTPx(p): + def get_energy_density_terms_FTPx(b, p): """Create energy density terms.""" # Density and energy terms probably do not exist when this is created # Use try/except to build term if not present @@ -318,7 +319,7 @@ def rule_edens(b, p): edens = b._energy_density_term = Expression(b.phase_list, rule=rule_edens) return edens[p] - b.get_energy_density_terms = get_energy_density_terms_FTPx + b.get_energy_density_terms = MethodType(get_energy_density_terms_FTPx, b) def default_material_balance_type_FTPx(): return MaterialBalanceType.componentTotal @@ -335,7 +336,7 @@ def get_material_flow_basis_FTPx(): b.get_material_flow_basis = get_material_flow_basis_FTPx - def define_state_vars_FTPx(): + def define_state_vars_FTPx(b): """Define state vars.""" return { "flow_mol": b.flow_mol, @@ -344,9 +345,9 @@ def define_state_vars_FTPx(): "pressure": b.pressure, } - b.define_state_vars = define_state_vars_FTPx + b.define_state_vars = MethodType(define_state_vars_FTPx, b) - def define_display_vars_FTPx(): + def define_display_vars_FTPx(b): """Define display vars.""" return { "Total Molar Flowrate": b.flow_mol, @@ -355,7 +356,7 @@ def define_display_vars_FTPx(): "Pressure": b.pressure, } - b.define_display_vars = define_display_vars_FTPx + b.define_display_vars = MethodType(define_display_vars_FTPx, b) def state_initialization(b): diff --git a/idaes/models/properties/modular_properties/state_definitions/FcPh.py b/idaes/models/properties/modular_properties/state_definitions/FcPh.py index c5ab892451..1c91f7920d 100644 --- a/idaes/models/properties/modular_properties/state_definitions/FcPh.py +++ b/idaes/models/properties/modular_properties/state_definitions/FcPh.py @@ -20,6 +20,7 @@ # TODO: Look into protected access issues # pylint: disable=protected-access +from types import MethodType from pyomo.environ import ( Constraint, Expression, @@ -283,13 +284,13 @@ def rule_phase_frac(b, p): # ------------------------------------------------------------------------- # General Methods - def get_material_flow_terms_FcPh(p, j): + def get_material_flow_terms_FcPh(b, p, j): """Create material flow terms for control volume.""" return b.flow_mol_phase_comp[p, j] - b.get_material_flow_terms = get_material_flow_terms_FcPh + b.get_material_flow_terms = MethodType(get_material_flow_terms_FcPh, b) - def get_enthalpy_flow_terms_FcPh(p): + def get_enthalpy_flow_terms_FcPh(b, p): """Create enthalpy flow terms.""" # enth_mol_phase probably does not exist when this is created # Use try/except to build flow term if not present @@ -303,9 +304,9 @@ def rule_eflow(b, p): eflow = b._enthalpy_flow_term = Expression(b.phase_list, rule=rule_eflow) return eflow[p] - b.get_enthalpy_flow_terms = get_enthalpy_flow_terms_FcPh + b.get_enthalpy_flow_terms = MethodType(get_enthalpy_flow_terms_FcPh, b) - def get_material_density_terms_FcPh(p, j): + def get_material_density_terms_FcPh(b, p, j): """Create material density terms.""" # dens_mol_phase probably does not exist when this is created # Use try/except to build term if not present @@ -321,9 +322,9 @@ def rule_mdens(b, p, j): ) return mdens[p, j] - b.get_material_density_terms = get_material_density_terms_FcPh + b.get_material_density_terms = MethodType(get_material_density_terms_FcPh, b) - def get_energy_density_terms_FcPh(p): + def get_energy_density_terms_FcPh(b, p): """Create energy density terms.""" # Density and energy terms probably do not exist when this is created # Use try/except to build term if not present @@ -337,7 +338,7 @@ def rule_edens(b, p): edens = b._energy_density_term = Expression(b.phase_list, rule=rule_edens) return edens[p] - b.get_energy_density_terms = get_energy_density_terms_FcPh + b.get_energy_density_terms = MethodType(get_energy_density_terms_FcPh, b) def default_material_balance_type_FcPh(): return MaterialBalanceType.componentTotal @@ -354,7 +355,7 @@ def get_material_flow_basis_FcPh(): b.get_material_flow_basis = get_material_flow_basis_FcPh - def define_state_vars_FcPh(): + def define_state_vars_FcPh(b): """Define state vars.""" return { "flow_mol_comp": b.flow_mol_comp, @@ -362,9 +363,9 @@ def define_state_vars_FcPh(): "pressure": b.pressure, } - b.define_state_vars = define_state_vars_FcPh + b.define_state_vars = MethodType(define_state_vars_FcPh, b) - def define_display_vars_FcPh(): + def define_display_vars_FcPh(b): """Define display vars.""" return { "Molar Flowrate": b.flow_mol_comp, @@ -372,7 +373,7 @@ def define_display_vars_FcPh(): "Pressure": b.pressure, } - b.define_display_vars = define_display_vars_FcPh + b.define_display_vars = MethodType(define_display_vars_FcPh, b) def define_default_scaling_factors(b): diff --git a/idaes/models/properties/modular_properties/state_definitions/FcTP.py b/idaes/models/properties/modular_properties/state_definitions/FcTP.py index 258e7162b6..39b523c138 100644 --- a/idaes/models/properties/modular_properties/state_definitions/FcTP.py +++ b/idaes/models/properties/modular_properties/state_definitions/FcTP.py @@ -20,6 +20,7 @@ # TODO: Look into protected access issues # pylint: disable=protected-access +from types import MethodType from pyomo.environ import ( Constraint, Expression, @@ -260,13 +261,13 @@ def rule_phase_frac(b, p): # ------------------------------------------------------------------------- # General Methods - def get_material_flow_terms_FcTP(p, j): + def get_material_flow_terms_FcTP(b, p, j): """Create material flow terms for control volume.""" return b.flow_mol_phase_comp[p, j] - b.get_material_flow_terms = get_material_flow_terms_FcTP + b.get_material_flow_terms = MethodType(get_material_flow_terms_FcTP, b) - def get_enthalpy_flow_terms_FcTP(p): + def get_enthalpy_flow_terms_FcTP(b, p): """Create enthalpy flow terms.""" # enth_mol_phase probably does not exist when this is created # Use try/except to build flow term if not present @@ -280,9 +281,9 @@ def rule_eflow(b, p): eflow = b._enthalpy_flow_term = Expression(b.phase_list, rule=rule_eflow) return eflow[p] - b.get_enthalpy_flow_terms = get_enthalpy_flow_terms_FcTP + b.get_enthalpy_flow_terms = MethodType(get_enthalpy_flow_terms_FcTP, b) - def get_material_density_terms_FcTP(p, j): + def get_material_density_terms_FcTP(b, p, j): """Create material density terms.""" # dens_mol_phase probably does not exist when this is created # Use try/except to build term if not present @@ -298,9 +299,9 @@ def rule_mdens(b, p, j): ) return mdens[p, j] - b.get_material_density_terms = get_material_density_terms_FcTP + b.get_material_density_terms = MethodType(get_material_density_terms_FcTP, b) - def get_energy_density_terms_FcTP(p): + def get_energy_density_terms_FcTP(b, p): """Create energy density terms.""" # Density and energy terms probably do not exist when this is created # Use try/except to build term if not present @@ -314,7 +315,7 @@ def rule_edens(b, p): edens = b._energy_density_term = Expression(b.phase_list, rule=rule_edens) return edens[p] - b.get_energy_density_terms = get_energy_density_terms_FcTP + b.get_energy_density_terms = MethodType(get_energy_density_terms_FcTP, b) def default_material_balance_type_FcTP(): return MaterialBalanceType.componentTotal @@ -331,7 +332,7 @@ def get_material_flow_basis_FcTP(): b.get_material_flow_basis = get_material_flow_basis_FcTP - def define_state_vars_FcTP(): + def define_state_vars_FcTP(b): """Define state vars.""" return { "flow_mol_comp": b.flow_mol_comp, @@ -339,9 +340,9 @@ def define_state_vars_FcTP(): "pressure": b.pressure, } - b.define_state_vars = define_state_vars_FcTP + b.define_state_vars = MethodType(define_state_vars_FcTP, b) - def define_display_vars_FcTP(): + def define_display_vars_FcTP(b): """Define display vars.""" return { "Molar Flowrate": b.flow_mol_comp, @@ -349,7 +350,7 @@ def define_display_vars_FcTP(): "Pressure": b.pressure, } - b.define_display_vars = define_display_vars_FcTP + b.define_display_vars = MethodType(define_display_vars_FcTP, b) def define_default_scaling_factors(b): diff --git a/idaes/models/properties/modular_properties/state_definitions/FpcTP.py b/idaes/models/properties/modular_properties/state_definitions/FpcTP.py index f36215f9d8..6e0d22b4ab 100644 --- a/idaes/models/properties/modular_properties/state_definitions/FpcTP.py +++ b/idaes/models/properties/modular_properties/state_definitions/FpcTP.py @@ -20,6 +20,7 @@ # TODO: Look into protected access issues # pylint: disable=protected-access +from types import MethodType from pyomo.environ import ( Constraint, Expression, @@ -183,13 +184,13 @@ def rule_phase_frac(b, p): # ------------------------------------------------------------------------- # General Methods - def get_material_flow_terms_FpcTP(p, j): + def get_material_flow_terms_FpcTP(b, p, j): """Create material flow terms for control volume.""" return b.flow_mol_phase_comp[p, j] - b.get_material_flow_terms = get_material_flow_terms_FpcTP + b.get_material_flow_terms = MethodType(get_material_flow_terms_FpcTP, b) - def get_enthalpy_flow_terms_FpcTP(p): + def get_enthalpy_flow_terms_FpcTP(b, p): """Create enthalpy flow terms.""" # enth_mol_phase probably does not exist when this is created # Use try/except to build flow term if not present @@ -203,9 +204,9 @@ def rule_eflow(b, p): eflow = b._enthalpy_flow_term = Expression(b.phase_list, rule=rule_eflow) return eflow[p] - b.get_enthalpy_flow_terms = get_enthalpy_flow_terms_FpcTP + b.get_enthalpy_flow_terms = MethodType(get_enthalpy_flow_terms_FpcTP, b) - def get_material_density_terms_FpcTP(p, j): + def get_material_density_terms_FpcTP(b, p, j): """Create material density terms.""" # dens_mol_phase probably does not exist when this is created # Use try/except to build term if not present @@ -221,9 +222,9 @@ def rule_mdens(b, p, j): ) return mdens[p, j] - b.get_material_density_terms = get_material_density_terms_FpcTP + b.get_material_density_terms = MethodType(get_material_density_terms_FpcTP, b) - def get_energy_density_terms_FpcTP(p): + def get_energy_density_terms_FpcTP(b, p): """Create energy density terms.""" # Density and energy terms probably do not exist when this is created # Use try/except to build term if not present @@ -237,7 +238,7 @@ def rule_edens(b, p): edens = b._energy_density_term = Expression(b.phase_list, rule=rule_edens) return edens[p] - b.get_energy_density_terms = get_energy_density_terms_FpcTP + b.get_energy_density_terms = MethodType(get_energy_density_terms_FpcTP, b) def default_material_balance_type_FpcTP(): return MaterialBalanceType.componentTotal @@ -254,7 +255,7 @@ def get_material_flow_basis_FpcTP(): b.get_material_flow_basis = get_material_flow_basis_FpcTP - def define_state_vars_FpcTP(): + def define_state_vars_FpcTP(b): """Define state vars.""" return { "flow_mol_phase_comp": b.flow_mol_phase_comp, @@ -262,9 +263,9 @@ def define_state_vars_FpcTP(): "pressure": b.pressure, } - b.define_state_vars = define_state_vars_FpcTP + b.define_state_vars = MethodType(define_state_vars_FpcTP, b) - def define_display_vars_FpcTP(): + def define_display_vars_FpcTP(b): """Define display vars.""" return { "Molar Flowrate": b.flow_mol_phase_comp, @@ -272,7 +273,7 @@ def define_display_vars_FpcTP(): "Pressure": b.pressure, } - b.define_display_vars = define_display_vars_FpcTP + b.define_display_vars = MethodType(define_display_vars_FpcTP, b) def state_initialization(b): diff --git a/idaes/models/properties/modular_properties/state_definitions/tests/test_FPhx.py b/idaes/models/properties/modular_properties/state_definitions/tests/test_FPhx.py index 125362959c..bdf1d6ff71 100644 --- a/idaes/models/properties/modular_properties/state_definitions/tests/test_FPhx.py +++ b/idaes/models/properties/modular_properties/state_definitions/tests/test_FPhx.py @@ -1254,3 +1254,44 @@ def test_define_display_vars(self, frame): "Molar Enthalpy": frame.props[1].enth_mol, "Pressure": frame.props[1].pressure, } + + @pytest.mark.unit + def test_cloning(self, frame): + """ + This function tests modular properties are cloned correctly using pyomo's + clone method. In particular, it tests that all the methods point to variables + on the clone, and not on the original model. + """ + blk = frame.clone() + + # Test get_material_flow_terms method + for p, j in blk.params._phase_component_set: + assert ( + blk.props[1].get_material_flow_terms(p, j).parent_block() + is blk.props[1] + ) + + # Test get_enthalpy_flow_terms method + for p in blk.params.phase_list: + assert ( + blk.props[1].get_enthalpy_flow_terms(p).parent_block() is blk.props[1] + ) + + # Test get_material_density_terms + for p, j in blk.params._phase_component_set: + assert ( + blk.props[1].get_material_density_terms(p, j).parent_block() + is blk.props[1] + ) + + # Test get_energy_Density_terms + for p in blk.params.phase_list: + assert ( + blk.props[1].get_energy_density_terms(p).parent_block() is blk.props[1] + ) + + for i in blk.props[1].define_state_vars().values(): + assert i.parent_block() is blk.props[1] + + for i in blk.props[1].define_display_vars().values(): + assert i.parent_block() is blk.props[1] diff --git a/idaes/models/properties/modular_properties/state_definitions/tests/test_FTPx.py b/idaes/models/properties/modular_properties/state_definitions/tests/test_FTPx.py index 7eacf9814e..1de2abd6c5 100644 --- a/idaes/models/properties/modular_properties/state_definitions/tests/test_FTPx.py +++ b/idaes/models/properties/modular_properties/state_definitions/tests/test_FTPx.py @@ -1379,6 +1379,47 @@ def test_unphysical_mol_fraction_fail(self, frame): frame.props[1] ) + @pytest.mark.unit + def test_cloning(self, frame): + """ + This function tests modular properties are cloned correctly using pyomo's + clone method. In particular, it tests that all the methods point to variables + on the clone, and not on the original model. + """ + blk = frame.clone() + + # Test get_material_flow_terms method + for p, j in blk.params._phase_component_set: + assert ( + blk.props[1].get_material_flow_terms(p, j).parent_block() + is blk.props[1] + ) + + # Test get_enthalpy_flow_terms method + for p in blk.params.phase_list: + assert ( + blk.props[1].get_enthalpy_flow_terms(p).parent_block() is blk.props[1] + ) + + # Test get_material_density_terms + for p, j in blk.params._phase_component_set: + assert ( + blk.props[1].get_material_density_terms(p, j).parent_block() + is blk.props[1] + ) + + # Test get_energy_Density_terms + for p in blk.params.phase_list: + assert ( + blk.props[1].get_energy_density_terms(p).parent_block() is blk.props[1] + ) + + for i in blk.props[1].define_state_vars().values(): + assert i.parent_block() is blk.props[1] + + for i in blk.props[1].define_display_vars().values(): + assert i.parent_block() is blk.props[1] + class TestModifiedRachfordRice(object): @pytest.fixture(scope="class") diff --git a/idaes/models/properties/modular_properties/state_definitions/tests/test_FcPh.py b/idaes/models/properties/modular_properties/state_definitions/tests/test_FcPh.py index c703f711b1..11d12c38b9 100644 --- a/idaes/models/properties/modular_properties/state_definitions/tests/test_FcPh.py +++ b/idaes/models/properties/modular_properties/state_definitions/tests/test_FcPh.py @@ -1357,3 +1357,44 @@ def test_define_display_vars(self, frame): "Molar Enthalpy": frame.props[1].enth_mol, "Pressure": frame.props[1].pressure, } + + @pytest.mark.unit + def test_cloning(self, frame): + """ + This function tests modular properties are cloned correctly using pyomo's + clone method. In particular, it tests that all the methods point to variables + on the clone, and not on the original model. + """ + blk = frame.clone() + + # Test get_material_flow_terms method + for p, j in blk.params._phase_component_set: + assert ( + blk.props[1].get_material_flow_terms(p, j).parent_block() + is blk.props[1] + ) + + # Test get_enthalpy_flow_terms method + for p in blk.params.phase_list: + assert ( + blk.props[1].get_enthalpy_flow_terms(p).parent_block() is blk.props[1] + ) + + # Test get_material_density_terms + for p, j in blk.params._phase_component_set: + assert ( + blk.props[1].get_material_density_terms(p, j).parent_block() + is blk.props[1] + ) + + # Test get_energy_Density_terms + for p in blk.params.phase_list: + assert ( + blk.props[1].get_energy_density_terms(p).parent_block() is blk.props[1] + ) + + for i in blk.props[1].define_state_vars().values(): + assert i.parent_block() is blk.props[1] + + for i in blk.props[1].define_display_vars().values(): + assert i.parent_block() is blk.props[1] diff --git a/idaes/models/properties/modular_properties/state_definitions/tests/test_FcTP.py b/idaes/models/properties/modular_properties/state_definitions/tests/test_FcTP.py index fa2b2b39ee..9355bf6515 100644 --- a/idaes/models/properties/modular_properties/state_definitions/tests/test_FcTP.py +++ b/idaes/models/properties/modular_properties/state_definitions/tests/test_FcTP.py @@ -1303,3 +1303,44 @@ def test_define_display_vars(self, frame): "Temperature": frame.props[1].temperature, "Pressure": frame.props[1].pressure, } + + @pytest.mark.unit + def test_cloning(self, frame): + """ + This function tests modular properties are cloned correctly using pyomo's + clone method. In particular, it tests that all the methods point to variables + on the clone, and not on the original model. + """ + blk = frame.clone() + + # Test get_material_flow_terms method + for p, j in blk.params._phase_component_set: + assert ( + blk.props[1].get_material_flow_terms(p, j).parent_block() + is blk.props[1] + ) + + # Test get_enthalpy_flow_terms method + for p in blk.params.phase_list: + assert ( + blk.props[1].get_enthalpy_flow_terms(p).parent_block() is blk.props[1] + ) + + # Test get_material_density_terms + for p, j in blk.params._phase_component_set: + assert ( + blk.props[1].get_material_density_terms(p, j).parent_block() + is blk.props[1] + ) + + # Test get_energy_Density_terms + for p in blk.params.phase_list: + assert ( + blk.props[1].get_energy_density_terms(p).parent_block() is blk.props[1] + ) + + for i in blk.props[1].define_state_vars().values(): + assert i.parent_block() is blk.props[1] + + for i in blk.props[1].define_display_vars().values(): + assert i.parent_block() is blk.props[1] diff --git a/idaes/models/properties/modular_properties/state_definitions/tests/test_FpcTP.py b/idaes/models/properties/modular_properties/state_definitions/tests/test_FpcTP.py index 5104375d93..22320c7698 100644 --- a/idaes/models/properties/modular_properties/state_definitions/tests/test_FpcTP.py +++ b/idaes/models/properties/modular_properties/state_definitions/tests/test_FpcTP.py @@ -1215,6 +1215,47 @@ def test_define_display_vars(self, frame): "Pressure": frame.props[1].pressure, } + @pytest.mark.unit + def test_cloning(self, frame): + """ + This function tests modular properties are cloned correctly using pyomo's + clone method. In particular, it tests that all the methods point to variables + on the clone, and not on the original model. + """ + blk = frame.clone() + + # Test get_material_flow_terms method + for p, j in blk.params._phase_component_set: + assert ( + blk.props[1].get_material_flow_terms(p, j).parent_block() + is blk.props[1] + ) + + # Test get_enthalpy_flow_terms method + for p in blk.params.phase_list: + assert ( + blk.props[1].get_enthalpy_flow_terms(p).parent_block() is blk.props[1] + ) + + # Test get_material_density_terms + for p, j in blk.params._phase_component_set: + assert ( + blk.props[1].get_material_density_terms(p, j).parent_block() + is blk.props[1] + ) + + # Test get_energy_Density_terms + for p in blk.params.phase_list: + assert ( + blk.props[1].get_energy_density_terms(p).parent_block() is blk.props[1] + ) + + for i in blk.props[1].define_state_vars().values(): + assert i.parent_block() is blk.props[1] + + for i in blk.props[1].define_display_vars().values(): + assert i.parent_block() is blk.props[1] + # ----------------------------------------------------------------------------- # Test example from Austin Ladshaw via WaterTAP project diff --git a/idaes/models/unit_models/separator.py b/idaes/models/unit_models/separator.py index 1f6d8c4ca9..c4ad8794c2 100644 --- a/idaes/models/unit_models/separator.py +++ b/idaes/models/unit_models/separator.py @@ -82,6 +82,7 @@ class EnergySplittingType(Enum): Enum of support energy split types. """ + none = 0 equal_temperature = 1 equal_molar_enthalpy = 2 enthalpy_split = 3 @@ -573,8 +574,9 @@ class SeparatorData(UnitModelBlockData): is not used for when ideal_separation == True. **default** - EnergySplittingType.equal_temperature. **Valid values:** { -**EnergySplittingType.equal_temperature** - outlet temperatures equal inlet -**EnergySplittingType.equal_molar_enthalpy** - oulet molar enthalpies equal +**EnergySplittingType.none** - no energy balance constraints, +**EnergySplittingType.equal_temperature** - outlet temperatures equal inlet, +**EnergySplittingType.equal_molar_enthalpy** - outlet molar enthalpies equal inlet, **EnergySplittingType.enthalpy_split** - apply split fractions to enthalpy flows. Does not work with component or phase-component splitting.}""", @@ -1059,10 +1061,12 @@ def material_splitting_eqn(b, t, o): def add_energy_splitting_constraints(self, mixed_block): """ - Creates constraints for splitting the energy flows - done by equating - temperatures in outlets. + Creates constraints for splitting the energy flows. """ - if self.config.energy_split_basis == EnergySplittingType.equal_temperature: + if self.config.energy_split_basis == EnergySplittingType.none: + # Do nothing and return + return + elif self.config.energy_split_basis == EnergySplittingType.equal_temperature: @self.Constraint( self.flowsheet().time, diff --git a/idaes/models/unit_models/tests/test_separator.py b/idaes/models/unit_models/tests/test_separator.py index ab8be95212..c9f1cc8c96 100644 --- a/idaes/models/unit_models/tests/test_separator.py +++ b/idaes/models/unit_models/tests/test_separator.py @@ -42,6 +42,7 @@ PhysicalParameterBlock, Phase, Component, + MaterialFlowBasis, ) from idaes.models.unit_models.separator import ( Separator, @@ -55,6 +56,10 @@ ConfigurationError, InitializationError, ) +from idaes.core.util.initialization import ( + fix_state_vars, + revert_state_vars, +) from idaes.models.properties.examples.saponification_thermo import ( SaponificationParameterBlock, @@ -726,6 +731,17 @@ def test_add_energy_splitting_constraints_enthalpy(self, build): assert isinstance(build.fs.sep.molar_enthalpy_equality_eqn, Constraint) assert len(build.fs.sep.molar_enthalpy_equality_eqn) == 2 + @pytest.mark.unit + def test_add_energy_splitting_constraints_none(self, build): + build.fs.sep.config.energy_split_basis = EnergySplittingType.none + assert build.fs.sep.config.energy_split_basis == EnergySplittingType.none + + build.fs.sep.add_split_fractions(build.outlet_list, build.fs.sep.mixed_state) + build.fs.sep.add_energy_splitting_constraints(build.fs.sep.mixed_state) + + assert not hasattr(build.fs.sep, "temperature_equality_eqn") + assert not hasattr(build.fs.sep, "molar_enthalpy_equality_eqn") + @pytest.mark.unit def test_add_momentum_splitting_constraints(self, build): build.fs.sep.add_split_fractions(build.outlet_list, build.fs.sep.mixed_state) @@ -3628,3 +3644,119 @@ def test_block_triangularization(self, model): assert not model.fs.unit.inlet.flow_mol[0].fixed assert not model.fs.unit.inlet.enth_mol[0].fixed assert not model.fs.unit.inlet.pressure[0].fixed + + +# ----------------------------------------------------------------------------- +# Li-Co Diafiltration example - only material balances +@declare_process_block_class("LiCoParameters") +class LiCoParameterData(PhysicalParameterBlock): + def build(self): + super().build() + + self.phase1 = Phase() + + self.solvent = Component() + self.Li = Component() + self.Co = Component() + + self._state_block_class = LiCoStateBlock + + @classmethod + def define_metadata(cls, obj): + obj.add_default_units( + { + "time": pyunits.hour, + "length": pyunits.m, + "mass": pyunits.kg, + "amount": pyunits.mol, + "temperature": pyunits.K, + } + ) + + +class LiCoSBlockBase(StateBlock): + def fix_initialization_states(blk): + return fix_state_vars(blk) + + +@declare_process_block_class("LiCoStateBlock", block_class=LiCoSBlockBase) +class LiCoStateBlock1Data(StateBlockData): + CONFIG = ConfigBlock(implicit=True) + + def build(self): + super().build() + + self.flow_vol = Var( + units=pyunits.m**3 / pyunits.hour, + bounds=(1e-8, None), + ) + self.conc_mass_solute = Var( + ["Li", "Co"], + units=pyunits.kg / pyunits.m**3, + bounds=(1e-8, None), + ) + + def get_material_flow_terms(self, p, j): + if j == "solvent": + # Assume constant density of pure water + return self.flow_vol * 1000 * pyunits.kg / pyunits.m**3 + else: + return self.flow_vol * self.conc_mass_solute[j] + + def get_material_flow_basis(self): + return MaterialFlowBasis.mass + + def define_state_vars(self): + return { + "flow_vol": self.flow_vol, + "conc_mass_solute": self.conc_mass_solute, + } + + +@pytest.mark.integration +def test_material_only(): + m = ConcreteModel() + m.fs = FlowsheetBlock(dynamic=False) + + m.fs.properties = LiCoParameters() + + m.fs.sep = Separator( + property_package=m.fs.properties, + material_balance_type=MaterialBalanceType.componentPhase, + split_basis=SplittingType.totalFlow, + num_outlets=2, + energy_split_basis=EnergySplittingType.none, + momentum_balance_type=MomentumBalanceType.none, + ideal_separation=False, + has_phase_equilibrium=False, + ) + + m.fs.sep.inlet.flow_vol[0].fix(10) + m.fs.sep.inlet.conc_mass_solute[0, "Li"].fix(2) + m.fs.sep.inlet.conc_mass_solute[0, "Co"].fix(3) + + m.fs.sep.split_fraction[0, "outlet_1"].fix(0.4) + + assert degrees_of_freedom(m) == 0 + + initializer = SeparatorInitializer() + initializer.initialize(m.fs.sep) + + assert initializer.summary[m.fs.sep]["status"] == InitializationStatus.Ok + + assert value(m.fs.sep.outlet_1.flow_vol[0]) == pytest.approx(4, rel=1e-6) + assert value(m.fs.sep.outlet_2.flow_vol[0]) == pytest.approx(6, rel=1e-6) + + assert value(m.fs.sep.outlet_1.conc_mass_solute[0, "Li"]) == pytest.approx( + 2, rel=1e-6 + ) + assert value(m.fs.sep.outlet_1.conc_mass_solute[0, "Co"]) == pytest.approx( + 3, rel=1e-6 + ) + + assert value(m.fs.sep.outlet_2.conc_mass_solute[0, "Li"]) == pytest.approx( + 2, rel=1e-6 + ) + assert value(m.fs.sep.outlet_2.conc_mass_solute[0, "Co"]) == pytest.approx( + 3, rel=1e-6 + ) diff --git a/idaes/models_extra/column_models/condenser.py b/idaes/models_extra/column_models/condenser.py index 50001b02fe..05129bc0b1 100644 --- a/idaes/models_extra/column_models/condenser.py +++ b/idaes/models_extra/column_models/condenser.py @@ -225,7 +225,7 @@ def build(self): # outlet pressure is a spec set by the user. # Get liquid and vapor phase objects from the property package - # to be used below. Avoids repition. + # to be used below. Avoids repetition. _liquid_list = [] _vapor_list = [] for p in self.config.property_package.phase_list: diff --git a/idaes/models_extra/column_models/plate_heat_exchanger.py b/idaes/models_extra/column_models/plate_heat_exchanger.py index 3d85022ab4..b54189d577 100644 --- a/idaes/models_extra/column_models/plate_heat_exchanger.py +++ b/idaes/models_extra/column_models/plate_heat_exchanger.py @@ -187,7 +187,7 @@ def build(self): initialize=0.2045, units=units_meta("length"), domain=PositiveReals, - doc="Port diamter", + doc="Port diameter", ) self.plate_therm_cond = Var( diff --git a/idaes/models_extra/column_models/reboiler.py b/idaes/models_extra/column_models/reboiler.py index efe17bbe7f..3e3420e4ca 100644 --- a/idaes/models_extra/column_models/reboiler.py +++ b/idaes/models_extra/column_models/reboiler.py @@ -207,7 +207,7 @@ def build(self): ) # Get liquid and vapor phase objects from the property package - # to be used below. Avoids repition. + # to be used below. Avoids repetition. _liquid_list = [] _vapor_list = [] for p in self.config.property_package.phase_list: diff --git a/idaes/models_extra/column_models/tray.py b/idaes/models_extra/column_models/tray.py index ea930d684a..ec741e4a69 100644 --- a/idaes/models_extra/column_models/tray.py +++ b/idaes/models_extra/column_models/tray.py @@ -220,7 +220,7 @@ def build(self): self._add_pressure_balance() # Get liquid and vapor phase objects from the property package - # to be used below. Avoids repition. + # to be used below. Avoids repetition. _liquid_list = [] _vapor_list = [] for p in self.config.property_package.phase_list: diff --git a/idaes/models_extra/gas_distribution/properties/natural_gas.py b/idaes/models_extra/gas_distribution/properties/natural_gas.py index c0b1c569be..b972d431cc 100644 --- a/idaes/models_extra/gas_distribution/properties/natural_gas.py +++ b/idaes/models_extra/gas_distribution/properties/natural_gas.py @@ -27,7 +27,7 @@ from pyomo.core.base.constraint import Constraint from pyomo.core.base.expression import Expression from pyomo.core.base.param import Param -from pyomo.core.expr.current import sqrt +from pyomo.core.expr import sqrt # Import IDAES cores from idaes.core import ( diff --git a/idaes/models_extra/gas_distribution/unit_models/pipeline.py b/idaes/models_extra/gas_distribution/unit_models/pipeline.py index 14a145f654..9e046c0bb6 100644 --- a/idaes/models_extra/gas_distribution/unit_models/pipeline.py +++ b/idaes/models_extra/gas_distribution/unit_models/pipeline.py @@ -31,7 +31,7 @@ from pyomo.core.base.units_container import units as pyunits from pyomo.core.base.reference import Reference from pyomo.dae.diffvar import DerivativeVar -from pyomo.core.expr.current import log10 +from pyomo.core.expr import log10 from pyomo.core.expr.numvalue import value as pyo_value from idaes.core import ( diff --git a/idaes/models_extra/power_generation/flowsheets/subcritical_power_plant/steam_cycle_flowsheet.py b/idaes/models_extra/power_generation/flowsheets/subcritical_power_plant/steam_cycle_flowsheet.py index 3679b6f84d..8ed413b4c1 100644 --- a/idaes/models_extra/power_generation/flowsheets/subcritical_power_plant/steam_cycle_flowsheet.py +++ b/idaes/models_extra/power_generation/flowsheets/subcritical_power_plant/steam_cycle_flowsheet.py @@ -845,7 +845,7 @@ def set_inputs(m): fs.fwh5.desuperheat.overall_heat_transfer_coefficient.fix(145) fs.fwh5.cooling.overall_heat_transfer_coefficient.fix(675) fs.fwh5.condense.tube.deltaP[:].fix(0) - # Inputs reqired for dynamic model + # Inputs required for dynamic model fs.fwh5.condense.level.fix(0.275) fs.fwh5.condense.heater_diameter.fix(1.4) fs.fwh5.condense.vol_frac_shell.fix(0.675) diff --git a/idaes/models_extra/power_generation/unit_models/helm/condenser_ntu.py b/idaes/models_extra/power_generation/unit_models/helm/condenser_ntu.py index 80cee87ca6..80cf3c7a90 100644 --- a/idaes/models_extra/power_generation/unit_models/helm/condenser_ntu.py +++ b/idaes/models_extra/power_generation/unit_models/helm/condenser_ntu.py @@ -82,7 +82,7 @@ def _make_heat_exchanger_config(config): @declare_process_block_class("HelmNtuCondenser", doc="Simple 0D condenser model.") class HelmNtuCondenserData(UnitModelBlockData): """ - Simple NTU condenser unit model. This model assumes the property pacakages + Simple NTU condenser unit model. This model assumes the property packages specified are Helmholtz EOS type. """ diff --git a/idaes/models_extra/power_generation/unit_models/steamheater.py b/idaes/models_extra/power_generation/unit_models/steamheater.py index 5380766388..a8c633a9f4 100644 --- a/idaes/models_extra/power_generation/unit_models/steamheater.py +++ b/idaes/models_extra/power_generation/unit_models/steamheater.py @@ -26,7 +26,7 @@ # Import Pyomo libraries from pyomo.common.config import ConfigBlock, ConfigValue, In, Bool from pyomo.environ import value, Var, Param, asin, cos, Reference -from pyomo.core.expr.current import Expr_if +from pyomo.core.expr import Expr_if from pyomo.dae import DerivativeVar # Import IDAES cores diff --git a/idaes/models_extra/power_generation/unit_models/waterwall_section.py b/idaes/models_extra/power_generation/unit_models/waterwall_section.py index d112602afa..d0ccfb93f2 100644 --- a/idaes/models_extra/power_generation/unit_models/waterwall_section.py +++ b/idaes/models_extra/power_generation/unit_models/waterwall_section.py @@ -37,7 +37,7 @@ ) from pyomo.dae import DerivativeVar from pyomo.common.config import ConfigBlock, ConfigValue, In, Bool -from pyomo.core.expr.current import Expr_if +from pyomo.core.expr import Expr_if # Import IDAES cores from idaes.core import ( diff --git a/setup.py b/setup.py index fd82f66646..3683e37d1a 100644 --- a/setup.py +++ b/setup.py @@ -126,7 +126,7 @@ def __getitem__(self, key): # Put abstract (non-versioned) deps here. # Concrete dependencies go in requirements[-dev].txt install_requires=[ - "pyomo>=6.6.1", + "pyomo @ https://github.com/IDAES/pyomo/archive/6.6.2.idaes.2023.07.28.zip", "pint", # required to use Pyomo units "networkx", # required to use Pyomo network "numpy",