From 9aa72bebbd8e972198f4f23efd4018badd9b86c6 Mon Sep 17 00:00:00 2001 From: Kadlec Date: Wed, 16 Apr 2025 15:01:08 -0600 Subject: [PATCH 01/57] debugging api call from locally hosted env --- julia_src/Manifest.toml | 244 +++++++++++++++++++++------------------- reoptjl/models.py | 204 +++++++++++++++++++++++++++++++++ 2 files changed, 332 insertions(+), 116 deletions(-) diff --git a/julia_src/Manifest.toml b/julia_src/Manifest.toml index 6729da562..8cf96f0c6 100644 --- a/julia_src/Manifest.toml +++ b/julia_src/Manifest.toml @@ -71,10 +71,10 @@ version = "0.4.7" uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" [[deps.BenchmarkTools]] -deps = ["JSON", "Logging", "Printf", "Profile", "Statistics", "UUIDs"] -git-tree-sha1 = "f1dff6729bc61f4d49e140da1af55dcd1ac97b2f" +deps = ["Compat", "JSON", "Logging", "Printf", "Profile", "Statistics", "UUIDs"] +git-tree-sha1 = "e38fbc49a620f5d0b660d7f543db1009fe0f8336" uuid = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" -version = "1.5.0" +version = "1.6.0" [[deps.BitFlags]] git-tree-sha1 = "0691e34b3bb8be9307330f88d1a3c3f25466c24d" @@ -89,15 +89,15 @@ version = "0.7.3" [[deps.Blosc_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Lz4_jll", "Zlib_jll", "Zstd_jll"] -git-tree-sha1 = "ef12cdd1c7fb7e1dfd6fa8fd60d4db6bc61d2f23" +git-tree-sha1 = "535c80f1c0847a4c967ea945fca21becc9de1522" uuid = "0b7ba130-8d10-5ba8-a3d6-c5182647fed9" -version = "1.21.6+2" +version = "1.21.7+0" [[deps.Bzip2_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "35abeca13bc0425cff9e59e229d971f5231323bf" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "1b96ea4a01afe0ea4090c5c8039690672dd13f2e" uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0" -version = "1.0.8+3" +version = "1.0.9+0" [[deps.CEnum]] git-tree-sha1 = "eb4cb44a499229b3b8426dcfb5dd85333951ff90" @@ -142,9 +142,9 @@ version = "0.8.5" [[deps.CodecZlib]] deps = ["TranscodingStreams", "Zlib_jll"] -git-tree-sha1 = "bce6804e5e6044c6daab27bb533d1295e4a2e759" +git-tree-sha1 = "962834c22b66e32aa10f7611c08c8ca4e20749a9" uuid = "944b1d66-785c-5afd-91f1-9de20f533193" -version = "0.7.6" +version = "0.7.8" [[deps.CoinUtils_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "OpenBLAS32_jll", "Pkg"] @@ -207,9 +207,9 @@ weakdeps = ["InverseFunctions"] [[deps.ConcurrentUtilities]] deps = ["Serialization", "Sockets"] -git-tree-sha1 = "f36e5e8fdffcb5646ea5da81495a5a7566005127" +git-tree-sha1 = "d9d26935a0bcffc87d2613ce14c527c99fc543fd" uuid = "f0e56b4a-5159-44fe-b623-3e5288b988bb" -version = "2.4.3" +version = "2.5.0" [[deps.ConstructionBase]] git-tree-sha1 = "76219f1ed5771adbb096743bff43fb5fdd4c1157" @@ -256,9 +256,9 @@ version = "1.7.0" [[deps.DataStructures]] deps = ["Compat", "InteractiveUtils", "OrderedCollections"] -git-tree-sha1 = "1d0a14036acb104d9e89698bd408f63ab58cdc82" +git-tree-sha1 = "4e1fe97fdaed23e9dc21d4d664bea76b65fc50a0" uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" -version = "0.18.20" +version = "0.18.22" [[deps.DataValueInterfaces]] git-tree-sha1 = "bfc1187b79289637fa0ef6d4436ebdfe6905cbd6" @@ -294,10 +294,9 @@ uuid = "3c3547ce-8d99-4f5e-a174-61eb10b00ae3" version = "0.3.23" [[deps.DocStringExtensions]] -deps = ["LibGit2"] -git-tree-sha1 = "2fb1e02f2b635d0845df5d7c167fec4dd739b00d" +git-tree-sha1 = "e7b7e6f178525d17c720ab9c081e4ef04429f860" uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" -version = "0.9.3" +version = "0.9.4" [[deps.DotEnv]] deps = ["PrecompileTools"] @@ -329,9 +328,9 @@ version = "0.1.5" [[deps.FileIO]] deps = ["Pkg", "Requires", "UUIDs"] -git-tree-sha1 = "2dd20384bf8c6d411b5c7370865b1e9b26cb2ea3" +git-tree-sha1 = "b66970a70db13f45b7e57fbda1736e1cf72174ea" uuid = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" -version = "1.16.6" +version = "1.17.0" weakdeps = ["HTTP"] [deps.FileIO.extensions] @@ -339,9 +338,9 @@ weakdeps = ["HTTP"] [[deps.FilePathsBase]] deps = ["Compat", "Dates"] -git-tree-sha1 = "2ec417fc319faa2d768621085cc1feebbdee686b" +git-tree-sha1 = "3bab2c5aa25e7840a4b065805c0cdfc01f3068d2" uuid = "48062228-2e41-5def-b9a4-89aafe57970f" -version = "0.9.23" +version = "0.9.24" weakdeps = ["Mmap", "Test"] [deps.FilePathsBase.extensions] @@ -414,7 +413,7 @@ uuid = "0329782f-3d07-4b52-b9f6-d3137cf03c7a" version = "1.0.2" [[deps.GhpGhx]] -git-tree-sha1 = "bddcbcddc9a4ae7ae4f1ea7d4d8ccf38507d4071" +git-tree-sha1 = "c2f3becdf925f287778fa088da6da01e307f6ce8" repo-rev = "main" repo-url = "https://github.com/NREL/GhpGhx.jl.git" uuid = "7ce85f02-24a8-4d69-a3f0-14b5daa7d30c" @@ -446,33 +445,33 @@ version = "0.17.2" [[deps.HDF5_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LazyArtifacts", "LibCURL_jll", "Libdl", "MPICH_jll", "MPIPreferences", "MPItrampoline_jll", "MicrosoftMPI_jll", "OpenMPI_jll", "OpenSSL_jll", "TOML", "Zlib_jll", "libaec_jll"] -git-tree-sha1 = "87bd95f99219dc3b86d4ee11a9a7bfa6075000a9" +git-tree-sha1 = "e94f84da9af7ce9c6be049e9067e511e17ff89ec" uuid = "0234f1f7-429e-5d53-9886-15a909be8d59" -version = "1.14.5+0" +version = "1.14.6+0" [[deps.HTTP]] -deps = ["Base64", "CodecZlib", "ConcurrentUtilities", "Dates", "ExceptionUnwrapping", "Logging", "LoggingExtras", "MbedTLS", "NetworkOptions", "OpenSSL", "Random", "SimpleBufferStream", "Sockets", "URIs", "UUIDs"] -git-tree-sha1 = "abbbb9ec3afd783a7cbd82ef01dcd088ea051398" +deps = ["Base64", "CodecZlib", "ConcurrentUtilities", "Dates", "ExceptionUnwrapping", "Logging", "LoggingExtras", "MbedTLS", "NetworkOptions", "OpenSSL", "PrecompileTools", "Random", "SimpleBufferStream", "Sockets", "URIs", "UUIDs"] +git-tree-sha1 = "c67b33b085f6e2faf8bf79a61962e7339a81129c" uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3" -version = "1.10.1" +version = "1.10.15" [[deps.HiGHS]] deps = ["HiGHS_jll", "MathOptInterface", "PrecompileTools", "SparseArrays"] -git-tree-sha1 = "fce13308f09771b160232903cad57be39a8a0ebb" +git-tree-sha1 = "219becee955e18b349be83d74bdd2e5ad4804d0f" uuid = "87dc4568-4c63-4d18-b0c0-bb2238e4078b" -version = "1.7.5" +version = "1.15.0" [[deps.HiGHS_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Zlib_jll"] -git-tree-sha1 = "f596ee3668df8587158bcaef1ae47bf75bc0fe39" +git-tree-sha1 = "72bceb63d4ae3683f091f812b6958a199c494a1b" uuid = "8fd58aa0-07eb-5a78-9b36-339c94fd15ea" -version = "1.6.0+1" +version = "1.10.0+0" [[deps.Hwloc_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "290232556f4ffb60ac3e476acf28e1a46e764742" +git-tree-sha1 = "f93a9ce66cd89c9ba7a4695a47fd93b4c6bc59fa" uuid = "e33a78d0-f292-5ffc-b300-72abe9b543c8" -version = "2.11.2+2" +version = "2.12.0+0" [[deps.ImageCore]] deps = ["AbstractFFTs", "ColorVectorSpace", "Colors", "FixedPointNumbers", "Graphics", "MappedArrays", "MosaicViews", "OffsetArrays", "PaddedViews", "Reexport"] @@ -525,14 +524,14 @@ version = "1.3.1" [[deps.Ipopt_jll]] deps = ["ASL_jll", "Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "MUMPS_seq_jll", "SPRAL_jll", "libblastrampoline_jll"] -git-tree-sha1 = "4f55ad688c698a4f77d892a1cb673f7e8a30f178" +git-tree-sha1 = "546c40fd3718c65d48296dd6cec98af9904e3ca4" uuid = "9cc047cb-c261-5740-88fc-0cf96f7bdcc7" -version = "300.1400.1700+0" +version = "300.1400.1400+0" [[deps.IrrationalConstants]] -git-tree-sha1 = "630b497eafcc20001bba38a4651b327dcfc491d2" +git-tree-sha1 = "e2222959fbc6c19554dc15174c81bf7bf3aa691c" uuid = "92d709cd-6900-40b7-9082-c6be49f344b6" -version = "0.2.2" +version = "0.2.4" [[deps.IterTools]] git-tree-sha1 = "42d5f897009e7ff2cf88db414a389e5ed1bdd023" @@ -562,17 +561,29 @@ git-tree-sha1 = "31e996f0a15c7b280ba9f76636b3ff9e2ae58c9a" uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" version = "0.21.4" +[[deps.JSON3]] +deps = ["Dates", "Mmap", "Parsers", "PrecompileTools", "StructTypes", "UUIDs"] +git-tree-sha1 = "196b41e5a854b387d99e5ede2de3fcb4d0422aae" +uuid = "0f8b85d8-7281-11e9-16c2-39a750bddbf1" +version = "1.14.2" + + [deps.JSON3.extensions] + JSON3ArrowExt = ["ArrowTypes"] + + [deps.JSON3.weakdeps] + ArrowTypes = "31f734f8-188a-4ce0-8406-c8a06bd891cd" + [[deps.JpegTurbo_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "3447a92280ecaad1bd93d3fce3d408b6cfff8913" +git-tree-sha1 = "eac1206917768cb54957c65a615460d87b455fc1" uuid = "aacddb02-875f-59d6-b918-886e6ef4fbf8" -version = "3.1.0+1" +version = "3.1.1+0" [[deps.JuMP]] -deps = ["LinearAlgebra", "MacroTools", "MathOptInterface", "MutableArithmetics", "OrderedCollections", "Printf", "SnoopPrecompile", "SparseArrays"] -git-tree-sha1 = "cd161958e8b47f9696a6b03f563afb4e5fe8f703" +deps = ["LinearAlgebra", "MacroTools", "MathOptInterface", "MutableArithmetics", "OrderedCollections", "PrecompileTools", "Printf", "SparseArrays"] +git-tree-sha1 = "c9ace86360c1dc0635de5f9e2ce5143b86c53311" uuid = "4076af6c-e467-56ae-b986-b466b2749572" -version = "1.17.0" +version = "1.25.0" [deps.JuMP.extensions] JuMPDimensionalDataExt = "DimensionalData" @@ -587,9 +598,9 @@ uuid = "88015f11-f218-50d7-93a8-a6af411a945d" version = "3.0.0+1" [[deps.LRUCache]] -git-tree-sha1 = "b3cc6698599b10e652832c2f23db3cab99d51b59" +git-tree-sha1 = "5519b95a490ff5fe629c4a7aa3b3dfc9160498b3" uuid = "8ac3fa9e-de4c-5943-b1dc-09c6b5f20637" -version = "1.6.1" +version = "1.6.2" weakdeps = ["Serialization"] [deps.LRUCache.extensions] @@ -685,16 +696,16 @@ uuid = "5ced341a-0733-55b8-9ab6-a4889d929147" version = "1.10.1+0" [[deps.METIS_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "1c20a46719c0dc4ec4e7021ca38f53e1ec9268d9" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "2eefa8baa858871ae7770c98c3c2a7e46daba5b4" uuid = "d00139f3-1899-568f-a2f0-47f597d42d70" -version = "5.1.2+1" +version = "5.1.3+0" [[deps.MPICH_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "Hwloc_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML"] -git-tree-sha1 = "7715e65c47ba3941c502bffb7f266a41a7f54423" +git-tree-sha1 = "3aa3210044138a1749dbd350a9ba8680869eb503" uuid = "7cb0a576-ebde-5e09-9194-50597f1243b4" -version = "4.2.3+0" +version = "4.3.0+1" [[deps.MPIPreferences]] deps = ["Libdl", "Preferences"] @@ -704,21 +715,20 @@ version = "0.1.11" [[deps.MPItrampoline_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML"] -git-tree-sha1 = "97aac4a518b6f01851f8821272780e1ba56fe90d" +git-tree-sha1 = "ff91ca13c7c472cef700f301c8d752bc2aaff1a8" uuid = "f1f71cc9-e9ae-5b93-9b94-4fe0e1ad3748" -version = "5.5.2+0" +version = "5.5.3+0" [[deps.MUMPS_seq_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "METIS_jll", "libblastrampoline_jll"] -git-tree-sha1 = "0eab12f94948ca67908aec14b9f2ebefd17463fe" +git-tree-sha1 = "840b83c65b27e308095c139a457373850b2f5977" uuid = "d7ed1dd3-d0ae-5e8e-bfb4-87a502085b8d" -version = "500.700.301+0" +version = "500.600.201+0" [[deps.MacroTools]] -deps = ["Markdown", "Random"] -git-tree-sha1 = "2fa9ee3e63fd3a4f7a9a4f4744a52f4856de82df" +git-tree-sha1 = "72aebe0b5051e5143a079a4685a46da330a40472" uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" -version = "0.5.13" +version = "0.5.15" [[deps.MappedArrays]] git-tree-sha1 = "2dab0221fe2b0f2cb6754eaa743cc266339f527e" @@ -730,10 +740,10 @@ deps = ["Base64"] uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" [[deps.MathOptInterface]] -deps = ["BenchmarkTools", "CodecBzip2", "CodecZlib", "DataStructures", "ForwardDiff", "JSON", "LinearAlgebra", "MutableArithmetics", "NaNMath", "OrderedCollections", "PrecompileTools", "Printf", "SparseArrays", "SpecialFunctions", "Test", "Unicode"] -git-tree-sha1 = "362ae34a5291a79e16b8eb87b5738532c5e799ff" +deps = ["BenchmarkTools", "CodecBzip2", "CodecZlib", "DataStructures", "ForwardDiff", "JSON3", "LinearAlgebra", "MutableArithmetics", "NaNMath", "OrderedCollections", "PrecompileTools", "Printf", "SparseArrays", "SpecialFunctions", "Test"] +git-tree-sha1 = "6723502b2135aa492a65be9633e694482a340ee7" uuid = "b8f27783-ece8-5eb3-8dc8-9495eed66fee" -version = "1.23.0" +version = "1.38.0" [[deps.MbedTLS]] deps = ["Dates", "MbedTLS_jll", "MozillaCACerts_jll", "NetworkOptions", "Random", "Sockets"] @@ -773,30 +783,30 @@ version = "2023.1.10" [[deps.MutableArithmetics]] deps = ["LinearAlgebra", "SparseArrays", "Test"] -git-tree-sha1 = "806eea990fb41f9b36f1253e5697aa645bf6a9f8" +git-tree-sha1 = "491bdcdc943fcbc4c005900d7463c9f216aabf4c" uuid = "d8a4904e-b15c-11e9-3269-09a3773c0cb0" -version = "1.4.0" +version = "1.6.4" [[deps.NaNMath]] deps = ["OpenLibm_jll"] -git-tree-sha1 = "0877504529a3e5c3343c6f8b4c0381e57e4387e4" +git-tree-sha1 = "9b8215b1ee9e78a293f99797cd31375471b2bcae" uuid = "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3" -version = "1.0.2" +version = "1.1.3" [[deps.Ncurses_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "3690e6c58c16ba676bcc9b5654762fe8a05db1c7" +git-tree-sha1 = "b5e7e7ad16adfe5f68530f9f641955b5b0f12bbb" uuid = "68e3532b-a499-55ff-9963-d1c0c0748b3a" -version = "6.5.0+1" +version = "6.5.1+0" [[deps.NetworkOptions]] uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" version = "1.2.0" [[deps.OffsetArrays]] -git-tree-sha1 = "5e1897147d1ff8d98883cda2be2187dcf57d8f0c" +git-tree-sha1 = "a414039192a155fb38c4599a60110f0018c6ec82" uuid = "6fe1bfb0-de20-5000-8ca7-80f57d26f881" -version = "1.15.0" +version = "1.16.0" [deps.OffsetArrays.extensions] OffsetArraysAdaptExt = "Adapt" @@ -828,9 +838,9 @@ version = "0.8.1+2" [[deps.OpenMPI_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "Hwloc_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML", "Zlib_jll"] -git-tree-sha1 = "2dace87e14256edb1dd0724ab7ba831c779b96bd" +git-tree-sha1 = "047b66eb62f3cae59ed260ebb9075a32a04350f1" uuid = "fe0851c0-eecd-5654-98d4-656369965a5c" -version = "5.0.6+0" +version = "5.0.7+2" [[deps.OpenSSL]] deps = ["BitFlags", "Dates", "MozillaCACerts_jll", "OpenSSL_jll", "Sockets"] @@ -840,20 +850,20 @@ version = "1.4.3" [[deps.OpenSSL_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "f58782a883ecbf9fb48dcd363f9ccd65f36c23a8" +git-tree-sha1 = "a9697f1d06cc3eb3fb3ad49cc67f2cfabaac31ea" uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95" -version = "3.0.15+2" +version = "3.0.16+0" [[deps.OpenSpecFun_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "418e63d434f5ca12b188bbb287dfbe10a5af1da4" +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl"] +git-tree-sha1 = "1346c9208249809840c91b26703912dff463d335" uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e" -version = "0.5.5+1" +version = "0.5.6+0" [[deps.OrderedCollections]] -git-tree-sha1 = "12f1439c4f986bb868acda6ea33ebc78e19b95ad" +git-tree-sha1 = "cc4054e898b852042d7b503313f7ad03de99c3dd" uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" -version = "1.7.0" +version = "1.8.0" [[deps.Osi_jll]] deps = ["Artifacts", "CoinUtils_jll", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "OpenBLAS32_jll", "Pkg"] @@ -922,9 +932,11 @@ uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" [[deps.REopt]] deps = ["ArchGDAL", "CSV", "CoolProp", "DataFrames", "Dates", "DelimitedFiles", "HTTP", "JLD", "JSON", "JuMP", "LinDistFlow", "LinearAlgebra", "Logging", "MathOptInterface", "Requires", "Roots", "Statistics", "TestEnv"] -git-tree-sha1 = "24f902b6f67ed1d4389d21b5d42f820036b182b4" +git-tree-sha1 = "88a07b108bb686d14787579bdbd3ddc30478184d" +repo-rev = "add-cst-reopt" +repo-url = "https://github.com/NREL/REopt.jl.git" uuid = "d36ad4e8-d74a-4f7a-ace1-eaea049febf6" -version = "0.51.0" +version = "0.47.2" [[deps.Random]] deps = ["SHA"] @@ -937,9 +949,9 @@ version = "0.3.2" [[deps.Readline_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Ncurses_jll"] -git-tree-sha1 = "69684dc9c2c69f7c515097841991362cca0739ea" +git-tree-sha1 = "6044f482a91c7aa2b82ab614aedd726be633ad05" uuid = "05236dd9-4125-5232-aa7c-9ec0c9b2c25a" -version = "8.2.1+1" +version = "8.2.13+0" [[deps.RecipesBase]] deps = ["PrecompileTools"] @@ -954,15 +966,15 @@ version = "1.2.2" [[deps.Requires]] deps = ["UUIDs"] -git-tree-sha1 = "838a3a4188e2ded87a4f9f184b4b0d78a1e91cb7" +git-tree-sha1 = "62389eeff14780bfe55195b7204c0d8738436d64" uuid = "ae029012-a4dd-5104-9daa-d747884805df" -version = "1.3.0" +version = "1.3.1" [[deps.Roots]] deps = ["Accessors", "CommonSolve", "Printf"] -git-tree-sha1 = "e52cf0872526c7a0b3e1af9c58a69b90e19b022e" +git-tree-sha1 = "3ac13765751ffc81e3531223782d9512f6023f71" uuid = "f2b01f46-fcfa-551c-844a-d8ac1e96c665" -version = "2.2.5" +version = "2.2.7" [deps.Roots.extensions] RootsChainRulesCoreExt = "ChainRulesCore" @@ -979,22 +991,22 @@ version = "2.2.5" SymPyPythonCall = "bc8888f7-b21e-4b7c-a06a-5d9c9496438c" [[deps.SCIP]] -deps = ["Ipopt_jll", "Libdl", "LinearAlgebra", "MathOptInterface", "SCIP_PaPILO_jll", "SCIP_jll"] -git-tree-sha1 = "ac0512c46cd91744f62463514f2c581025ea5b93" +deps = ["Libdl", "LinearAlgebra", "MathOptInterface", "OpenBLAS32_jll", "SCIP_PaPILO_jll", "SCIP_jll"] +git-tree-sha1 = "34fc53e96b77a9abb54d1303d1b0b8999e5c4514" uuid = "82193955-e24f-5292-bf16-6f2c5261a85f" -version = "0.11.6" +version = "0.12.3" [[deps.SCIP_PaPILO_jll]] -deps = ["Artifacts", "Bzip2_jll", "CompilerSupportLibraries_jll", "GMP_jll", "Ipopt_jll", "JLLWrappers", "Libdl", "OpenBLAS32_jll", "Pkg", "Readline_jll", "Zlib_jll", "bliss_jll", "boost_jll", "oneTBB_jll"] -git-tree-sha1 = "7705b5779724f35d78351548f24c7f7656e61bc2" +deps = ["Artifacts", "Bzip2_jll", "CompilerSupportLibraries_jll", "GMP_jll", "Ipopt_jll", "JLLWrappers", "Libdl", "OpenBLAS32_jll", "Readline_jll", "Zlib_jll", "bliss_jll", "boost_jll", "oneTBB_jll"] +git-tree-sha1 = "6fc56a56f9bccaa809d96250dba5eab909a60f6f" uuid = "fc9abe76-a5e6-5fed-b0b7-a12f309cf031" -version = "0.1.0+3" +version = "900.200.100+0" [[deps.SCIP_jll]] -deps = ["Artifacts", "Bzip2_jll", "CompilerSupportLibraries_jll", "GMP_jll", "Ipopt_jll", "JLLWrappers", "Libdl", "Pkg", "Readline_jll", "Zlib_jll", "bliss_jll", "boost_jll"] -git-tree-sha1 = "4a23f926d711535640963aea90a3f5d931ae52c7" +deps = ["Artifacts", "Bzip2_jll", "CompilerSupportLibraries_jll", "GMP_jll", "Ipopt_jll", "JLLWrappers", "Libdl", "Readline_jll", "Zlib_jll", "boost_jll"] +git-tree-sha1 = "f87b83dd8bd166c0904d64441fc424073c65539d" uuid = "e5ac4fe4-a920-5659-9bf8-f9f73e9e79ce" -version = "0.2.1+0" +version = "900.200.100+0" [[deps.SHA]] uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" @@ -1002,15 +1014,15 @@ version = "0.7.0" [[deps.SPRAL_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "Hwloc_jll", "JLLWrappers", "Libdl", "METIS_jll", "libblastrampoline_jll"] -git-tree-sha1 = "11f3da4b25efacd1cec8e263421f2a9003a5e8e0" +git-tree-sha1 = "34b9dacd687cace8aa4d550e3e9bb8615f1a61e9" uuid = "319450e9-13b8-58e8-aa9f-8fd1420848ab" -version = "2024.5.8+0" +version = "2024.1.18+0" [[deps.SQLite_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Zlib_jll"] -git-tree-sha1 = "7b5b0b963000117848dfe9199bbe4f528e37c5fd" +git-tree-sha1 = "9a325057cdb9b066f1f96dc77218df60fe3007cb" uuid = "76ed43ae-9a5d-5a62-8c75-30186b810ce8" -version = "3.47.2+1" +version = "3.48.0+0" [[deps.SentinelArrays]] deps = ["Dates", "Random"] @@ -1026,12 +1038,6 @@ git-tree-sha1 = "f305871d2f381d21527c770d4788c06c097c9bc1" uuid = "777ac1f9-54b0-4bf8-805c-2214025038e7" version = "1.2.0" -[[deps.SnoopPrecompile]] -deps = ["Preferences"] -git-tree-sha1 = "e760a70afdcd461cf01a575947738d359234665c" -uuid = "66db9d55-30c0-4569-8b51-7e840670fc0c" -version = "1.0.3" - [[deps.Sockets]] uuid = "6462fe0b-24de-5631-8697-dd941f90decc" @@ -1080,6 +1086,12 @@ git-tree-sha1 = "725421ae8e530ec29bcbdddbe91ff8053421d023" uuid = "892a3eda-7b42-436c-8928-eab12a02cf0e" version = "0.4.1" +[[deps.StructTypes]] +deps = ["Dates", "UUIDs"] +git-tree-sha1 = "159331b30e94d7b11379037feeb9b690950cace8" +uuid = "856f2bd8-1eba-4b0a-8007-ebc267875bd4" +version = "1.11.0" + [[deps.SuiteSparse_jll]] deps = ["Artifacts", "Libdl", "libblastrampoline_jll"] uuid = "bea87d4a-7f5b-5778-9afe-8cc45184846c" @@ -1129,9 +1141,9 @@ uuid = "3bb67fe8-82b1-5028-8e26-92a6c54297fa" version = "0.11.3" [[deps.URIs]] -git-tree-sha1 = "67db6cc7b3821e19ebe75791a9dd19c9b1188f2b" +git-tree-sha1 = "cbbebadbcc76c5ca1cc4b4f3b0614b3e603b5000" uuid = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4" -version = "1.5.1" +version = "1.5.2" [[deps.UUIDs]] deps = ["Random", "SHA"] @@ -1163,10 +1175,10 @@ uuid = "76eceee3-57b5-4d4a-8e66-0e911cebbf60" version = "1.6.1" [[deps.Xpress]] -deps = ["Libdl", "LinearAlgebra", "MathOptInterface", "SparseArrays"] -git-tree-sha1 = "29c47b54b6938852a598fc6761ed927aad61f10e" +deps = ["Libdl", "MathOptInterface"] +git-tree-sha1 = "f48ea69baa747f63b0d08c89ce7bc38d8ef62bd4" uuid = "9e70acf3-d6c9-5be6-b5bd-4e2c73e3e054" -version = "0.16.2" +version = "0.17.1" [[deps.Zlib_jll]] deps = ["Libdl"] @@ -1175,9 +1187,9 @@ version = "1.2.13+1" [[deps.Zstd_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "7dc5adc3f9bfb9b091b7952f4f6048b7e37acafc" +git-tree-sha1 = "446b23e73536f84e8037f5dce465e92275f6a308" uuid = "3161d3a3-bdf6-5164-811a-617609db77b4" -version = "1.5.6+2" +version = "1.5.7+1" [[deps.bliss_jll]] deps = ["Artifacts", "GMP_jll", "JLLWrappers", "Libdl", "Pkg"] @@ -1187,9 +1199,9 @@ version = "0.77.0+1" [[deps.boost_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] -git-tree-sha1 = "7a89efe0137720ca82f99e8daa526d23120d0d37" +git-tree-sha1 = "d9484c66c733c1c84f1d4cfef538d3c7b9d32199" uuid = "28df3c45-c428-5900-9ff8-a3135698ca75" -version = "1.76.0+1" +version = "1.79.0+3" [[deps.libaec_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] @@ -1210,9 +1222,9 @@ version = "100.700.100+0" [[deps.libpng_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Zlib_jll"] -git-tree-sha1 = "055a96774f383318750a1a5e10fd4151f04c29c5" +git-tree-sha1 = "068dfe202b0a05b8332f1e8e6b4080684b9c7700" uuid = "b53b4c65-9356-5827-b1ea-8c7a1a84506f" -version = "1.6.46+0" +version = "1.6.47+0" [[deps.nghttp2_jll]] deps = ["Artifacts", "Libdl"] @@ -1221,9 +1233,9 @@ version = "1.52.0+1" [[deps.oneTBB_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "7d0ea0f4895ef2f5cb83645fa689e52cb55cf493" +git-tree-sha1 = "d5a767a3bb77135a99e433afe0eb14cd7f6914c3" uuid = "1317d2d5-d96f-522e-a858-c73665f53c3e" -version = "2021.12.0+0" +version = "2022.0.0+0" [[deps.p7zip_jll]] deps = ["Artifacts", "Libdl"] diff --git a/reoptjl/models.py b/reoptjl/models.py index 47d088f7a..a77c92d5f 100644 --- a/reoptjl/models.py +++ b/reoptjl/models.py @@ -2991,6 +2991,210 @@ class PVOutputs(BaseModel, models.Model): default=list, blank=True ) +class CSTInputs(BaseModel, models.Model): + key = "CST" + meta = models.ForeignKey( + to=APIMeta, + on_delete=models.CASCADE, + related_name="CSTInputs", + unique=False + ) + + tech_type = models.TextField( + blank=True, + default="ptc", + help_text="Type of CST you want to implement into your system" + ) + min_kw = models.FloatField( + default=0, + validators=[ + MinValueValidator(0), + MaxValueValidator(1.0e9) + ], + blank=True, + help_text="Minimum CST size constraint for optimization." + ) + max_kw = models.FloatField( + default=1.0e9, + validators=[ + MinValueValidator(0), + MaxValueValidator(1.0e9) + ], + blank=True, + help_text="Maximum CST size constraint for optimization (upper bound on additional capacity beyond existing_kw). Set to zero to disable PV" + ) + acres_per_kw = models.FloatField( + default=0.006, + validators=[ + MinValueValidator(0), + MaxValueValidator(1.0e3) + ], + blank=True, + help_text="Power density for CST" + ) + installed_cost_per_kw = models.FloatField( + default=1200, + validators=[ + MinValueValidator(0), + MaxValueValidator(1.0e5) + ], + blank=True, + help_text="Installed CST cost in $/kW" + ) + om_cost_per_kw = models.FloatField( + default=18, + validators=[ + MinValueValidator(0), + MaxValueValidator(1.0e3) + ], + blank=True, + help_text="Annual CST operations and maintenance costs in $/kW" + ) + om_cost_per_kwh = models.FloatField( + default=18, + validators=[ + MinValueValidator(0), + MaxValueValidator(1.0e3) + ], + blank=True, + help_text="Annual CST operations and maintenance costs in $/kWh" + ) + macrs_option_years = models.IntegerField( + default=MACRS_YEARS_CHOICES.FIVE, + choices=MACRS_YEARS_CHOICES.choices, + blank=True, + help_text="Duration over which accelerated depreciation will occur. Set to zero to disable" + ) + macrs_bonus_fraction = models.FloatField( + default=0.6, + validators=[ + MinValueValidator(0), + MaxValueValidator(1) + ], + blank=True, + help_text="Percent of upfront project costs to depreciate in year one in addition to scheduled depreciation" + ) + capacity_factor_series = ArrayField( + models.FloatField( + blank=True + ), + default=list, blank=True, + help_text=("Optional user-defined capacity factors for CST.") + ) + elec_consumption_factor_series = ArrayField( + models.FloatField( + blank=True + ), + default=list, blank=True, + help_text=("Optional user-defined electricity consumption factors for CST.") + ) + can_supply_steam_turbine = models.BooleanField( + default=False, + null=True, + blank=True, + help_text="Boolean indicator if CST can supply steam to the steam turbine for electric production" + ) + can_serve_dhw = models.BooleanField( + default=True, + null=True, + blank=True, + help_text="Boolean indicator if CST can serve hot water load" + ) + can_serve_space_heating = models.BooleanField( + default=True, + null=True, + blank=True, + help_text="Boolean indicator if CST can serve space heating load" + ) + can_serve_process_heat = models.BooleanField( + default=True, + null=True, + blank=True, + help_text="Boolean indicator if CST can serve process heat load" + ) + charge_storage_only = models.BooleanField( + default=True, + null=True, + blank=True, + help_text="Boolean indicator if CST can only supply hot TES" + ) + emissions_factor_lb_CO2_per_mmbtu = models.FloatField( + validators=[ + MinValueValidator(0), + MaxValueValidator(1e4) + ], + blank=True, + null=True, + help_text="Pounds of CO2 emitted per MMBTU of fuel burned." + ) + emissions_factor_lb_NOx_per_mmbtu = models.FloatField( + validators=[ + MinValueValidator(0), + MaxValueValidator(1e4) + ], + blank=True, + null=True, + help_text="Pounds of CO2 emitted per MMBTU of fuel burned." + ) + emissions_factor_lb_SO2_per_mmbtu = models.FloatField( + validators=[ + MinValueValidator(0), + MaxValueValidator(1e4) + ], + blank=True, + null=True, + help_text="Pounds of CO2 emitted per MMBTU of fuel burned." + ) + emissions_factor_lb_PM25_per_mmbtu = models.FloatField( + validators=[ + MinValueValidator(0), + MaxValueValidator(1e4) + ], + blank=True, + null=True, + help_text="Pounds of CO2 emitted per MMBTU of fuel burned." + ) + +class CSTOutputs(BaseModel, models.Model): + key = "CSTOutputs" + meta = models.ForeignKey( + to=APIMeta, + on_delete=models.CASCADE, + related_name="CSTOutputs", + unique=False + ) + size_kw = models.FloatField(null=True, blank=True) + annual_electric_consumption_kwh = models.FloatField(null=True, blank=True) + annual_thermal_production_mmbtu = models.FloatField(null=True, blank=True) + thermal_production_series_mmbtu_per_hour = ArrayField( + models.FloatField(null=True, blank=True), + default=list, blank=True + ) + electric_consumption_series_kw = ArrayField( + models.FloatField(null=True, blank=True), + default=list, blank=True + ) + thermal_to_storage_series_mmbtu_per_hour = ArrayField( + models.FloatField(null=True, blank=True), + blank=True, default=list + ) + thermal_to_hot_sensible_tes_storage_series_mmbtu_per_hour = ArrayField( + models.FloatField(null=True, blank=True), + blank=True, default=list + ) + thermal_to_steamturbine_series_mmbtu_per_hour = ArrayField( + models.FloatField(null=True, blank=True), + blank=True, default=list + ) + thermal_curtailed_series_mmbtu_per_hour = ArrayField( + models.FloatField(null=True, blank=True), + blank=True, default=list + ) + thermal_to_load_series_mmbtu_per_hour = ArrayField( + models.FloatField(null=True, blank=True), + default=list, blank=True + ) + class WindInputs(BaseModel, models.Model): key = "Wind" From c54403ec5f285370689307fd911e7b0bc7f8ac0f Mon Sep 17 00:00:00 2001 From: Kadlec Date: Wed, 23 Apr 2025 10:49:05 -0600 Subject: [PATCH 02/57] add cst inputs/outputs --- .../migrations/0081_cstoutputs_cstinputs.py | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 reoptjl/migrations/0081_cstoutputs_cstinputs.py diff --git a/reoptjl/migrations/0081_cstoutputs_cstinputs.py b/reoptjl/migrations/0081_cstoutputs_cstinputs.py new file mode 100644 index 000000000..9c71d9685 --- /dev/null +++ b/reoptjl/migrations/0081_cstoutputs_cstinputs.py @@ -0,0 +1,63 @@ +# Generated by Django 4.0.7 on 2025-04-23 16:47 + +import django.contrib.postgres.fields +import django.core.validators +from django.db import migrations, models +import django.db.models.deletion +import reoptjl.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('reoptjl', '0080_electricloadoutputs_annual_electric_load_with_thermal_conversions_kwh_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='CSTOutputs', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('size_kw', models.FloatField(blank=True, null=True)), + ('annual_electric_consumption_kwh', models.FloatField(blank=True, null=True)), + ('annual_thermal_production_mmbtu', models.FloatField(blank=True, null=True)), + ('thermal_production_series_mmbtu_per_hour', django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), blank=True, default=list, size=None)), + ('electric_consumption_series_kw', django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), blank=True, default=list, size=None)), + ('thermal_to_storage_series_mmbtu_per_hour', django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), blank=True, default=list, size=None)), + ('thermal_to_hot_sensible_tes_storage_series_mmbtu_per_hour', django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), blank=True, default=list, size=None)), + ('thermal_to_steamturbine_series_mmbtu_per_hour', django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), blank=True, default=list, size=None)), + ('thermal_curtailed_series_mmbtu_per_hour', django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), blank=True, default=list, size=None)), + ('thermal_to_load_series_mmbtu_per_hour', django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), blank=True, default=list, size=None)), + ('meta', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='CSTOutputs', to='reoptjl.apimeta')), + ], + bases=(reoptjl.models.BaseModel, models.Model), + ), + migrations.CreateModel( + name='CSTInputs', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('tech_type', models.TextField(blank=True, default='ptc', help_text='Type of CST you want to implement into your system')), + ('min_kw', models.FloatField(blank=True, default=0, help_text='Minimum CST size constraint for optimization.', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1000000000.0)])), + ('max_kw', models.FloatField(blank=True, default=1000000000.0, help_text='Maximum CST size constraint for optimization (upper bound on additional capacity beyond existing_kw). Set to zero to disable PV', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1000000000.0)])), + ('acres_per_kw', models.FloatField(blank=True, default=0.006, help_text='Power density for CST', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1000.0)])), + ('installed_cost_per_kw', models.FloatField(blank=True, default=1200, help_text='Installed CST cost in $/kW', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(100000.0)])), + ('om_cost_per_kw', models.FloatField(blank=True, default=18, help_text='Annual CST operations and maintenance costs in $/kW', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1000.0)])), + ('om_cost_per_kwh', models.FloatField(blank=True, default=18, help_text='Annual CST operations and maintenance costs in $/kWh', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1000.0)])), + ('macrs_option_years', models.IntegerField(blank=True, choices=[(0, 'Zero'), (5, 'Five'), (7, 'Seven')], default=5, help_text='Duration over which accelerated depreciation will occur. Set to zero to disable')), + ('macrs_bonus_fraction', models.FloatField(blank=True, default=0.6, help_text='Percent of upfront project costs to depreciate in year one in addition to scheduled depreciation', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1)])), + ('capacity_factor_series', django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True), blank=True, default=list, help_text='Optional user-defined capacity factors for CST.', size=None)), + ('elec_consumption_factor_series', django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True), blank=True, default=list, help_text='Optional user-defined electricity consumption factors for CST.', size=None)), + ('can_supply_steam_turbine', models.BooleanField(blank=True, default=False, help_text='Boolean indicator if CST can supply steam to the steam turbine for electric production', null=True)), + ('can_serve_dhw', models.BooleanField(blank=True, default=True, help_text='Boolean indicator if CST can serve hot water load', null=True)), + ('can_serve_space_heating', models.BooleanField(blank=True, default=True, help_text='Boolean indicator if CST can serve space heating load', null=True)), + ('can_serve_process_heat', models.BooleanField(blank=True, default=True, help_text='Boolean indicator if CST can serve process heat load', null=True)), + ('charge_storage_only', models.BooleanField(blank=True, default=True, help_text='Boolean indicator if CST can only supply hot TES', null=True)), + ('emissions_factor_lb_CO2_per_mmbtu', models.FloatField(blank=True, help_text='Pounds of CO2 emitted per MMBTU of fuel burned.', null=True, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(10000.0)])), + ('emissions_factor_lb_NOx_per_mmbtu', models.FloatField(blank=True, help_text='Pounds of CO2 emitted per MMBTU of fuel burned.', null=True, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(10000.0)])), + ('emissions_factor_lb_SO2_per_mmbtu', models.FloatField(blank=True, help_text='Pounds of CO2 emitted per MMBTU of fuel burned.', null=True, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(10000.0)])), + ('emissions_factor_lb_PM25_per_mmbtu', models.FloatField(blank=True, help_text='Pounds of CO2 emitted per MMBTU of fuel burned.', null=True, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(10000.0)])), + ('meta', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='CSTInputs', to='reoptjl.apimeta')), + ], + bases=(reoptjl.models.BaseModel, models.Model), + ), + ] From d455eb187b1d1048aee4d8cbe46ee57accce505a Mon Sep 17 00:00:00 2001 From: Kadlec Date: Tue, 6 May 2025 13:39:20 -0600 Subject: [PATCH 03/57] adding in correct usage for CST inputs/outputs --- julia_src/Manifest.toml | 4 ++-- reoptjl/models.py | 4 ++++ reoptjl/src/process_results.py | 4 +++- reoptjl/validators.py | 3 ++- reoptjl/views.py | 6 +++++- 5 files changed, 16 insertions(+), 5 deletions(-) diff --git a/julia_src/Manifest.toml b/julia_src/Manifest.toml index 8cf96f0c6..a4a411c0b 100644 --- a/julia_src/Manifest.toml +++ b/julia_src/Manifest.toml @@ -932,11 +932,11 @@ uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" [[deps.REopt]] deps = ["ArchGDAL", "CSV", "CoolProp", "DataFrames", "Dates", "DelimitedFiles", "HTTP", "JLD", "JSON", "JuMP", "LinDistFlow", "LinearAlgebra", "Logging", "MathOptInterface", "Requires", "Roots", "Statistics", "TestEnv"] -git-tree-sha1 = "88a07b108bb686d14787579bdbd3ddc30478184d" +git-tree-sha1 = "ff57946772fdc45adfef3ce0b6eb80a2926a2ae2" repo-rev = "add-cst-reopt" repo-url = "https://github.com/NREL/REopt.jl.git" uuid = "d36ad4e8-d74a-4f7a-ace1-eaea049febf6" -version = "0.47.2" +version = "0.51.1" [[deps.Random]] deps = ["SHA"] diff --git a/reoptjl/models.py b/reoptjl/models.py index a77c92d5f..d0cfdda6a 100644 --- a/reoptjl/models.py +++ b/reoptjl/models.py @@ -8607,6 +8607,10 @@ def filter_none_and_empty_array(d:dict): try: d["ASHPWaterHeater"] = filter_none_and_empty_array(meta.ASHPWaterHeaterInputs.dict) except: pass + try: d["CST"] = filter_none_and_empty_array(meta.CSTInputs.dict) + except: pass + + return d ''' diff --git a/reoptjl/src/process_results.py b/reoptjl/src/process_results.py index 5b5732d69..ae7a56f84 100644 --- a/reoptjl/src/process_results.py +++ b/reoptjl/src/process_results.py @@ -8,7 +8,7 @@ REoptjlMessageOutputs, AbsorptionChillerOutputs, BoilerOutputs, SteamTurbineInputs, \ SteamTurbineOutputs, GHPInputs, GHPOutputs, ExistingChillerInputs, \ ElectricHeaterOutputs, ASHPSpaceHeaterOutputs, ASHPWaterHeaterOutputs, \ - SiteInputs, ASHPSpaceHeaterInputs, ASHPWaterHeaterInputs + SiteInputs, ASHPSpaceHeaterInputs, ASHPWaterHeaterInputs, CSTOutputs import numpy as np import sys import traceback as tb @@ -48,6 +48,8 @@ def process_results(results: dict, run_uuid: str) -> None: GeneratorOutputs.create(meta=meta, **results["Generator"]).save() if "Wind" in results.keys(): WindOutputs.create(meta=meta, **results["Wind"]).save() + if "CST" in results.keys(): + CSTOutputs.create(meta=meta, **results["CST"]).save() if "Boiler" in results.keys(): BoilerOutputs.create(meta=meta, **results["Boiler"]).save() if "ExistingBoiler" in results.keys(): diff --git a/reoptjl/validators.py b/reoptjl/validators.py index a65a60b02..84df48836 100644 --- a/reoptjl/validators.py +++ b/reoptjl/validators.py @@ -2,7 +2,7 @@ import logging import pandas as pd from reoptjl.models import MAX_BIG_NUMBER, APIMeta, ExistingBoilerInputs, UserProvidedMeta, SiteInputs, Settings, ElectricLoadInputs, ElectricTariffInputs, \ - FinancialInputs, BaseModel, Message, ElectricUtilityInputs, PVInputs, ElectricStorageInputs, GeneratorInputs, WindInputs, SpaceHeatingLoadInputs, \ + FinancialInputs, BaseModel, Message, ElectricUtilityInputs, PVInputs, CSTInputs, ElectricStorageInputs, GeneratorInputs, WindInputs, SpaceHeatingLoadInputs, \ DomesticHotWaterLoadInputs, CHPInputs, CoolingLoadInputs, ExistingChillerInputs, HotThermalStorageInputs, ColdThermalStorageInputs, \ AbsorptionChillerInputs, BoilerInputs, SteamTurbineInputs, GHPInputs, ProcessHeatLoadInputs, ElectricHeaterInputs, ASHPSpaceHeaterInputs, \ ASHPWaterHeaterInputs @@ -66,6 +66,7 @@ def __init__(self, raw_inputs: dict, ghpghx_inputs_validation_errors=None): FinancialInputs, ElectricUtilityInputs, PVInputs, + CSTInputs, ElectricStorageInputs, GeneratorInputs, WindInputs, diff --git a/reoptjl/views.py b/reoptjl/views.py index 566733f5e..6a23ff4af 100644 --- a/reoptjl/views.py +++ b/reoptjl/views.py @@ -8,7 +8,7 @@ import re from django.http import JsonResponse, HttpResponse from reo.exceptions import UnexpectedError -from reoptjl.models import Settings, PVInputs, ElectricStorageInputs, WindInputs, GeneratorInputs, ElectricLoadInputs,\ +from reoptjl.models import Settings, PVInputs, CSTInputs, ElectricStorageInputs, WindInputs, GeneratorInputs, ElectricLoadInputs,\ ElectricTariffInputs, ElectricUtilityInputs, SpaceHeatingLoadInputs, PVOutputs, ElectricStorageOutputs,\ WindOutputs, ExistingBoilerInputs, GeneratorOutputs, ElectricTariffOutputs, ElectricUtilityOutputs, \ ElectricLoadOutputs, ExistingBoilerOutputs, DomesticHotWaterLoadInputs, SiteInputs, SiteOutputs, APIMeta, \ @@ -62,6 +62,7 @@ def help(request): d["ElectricTariff"] = ElectricTariffInputs.info_dict(ElectricTariffInputs) d["ElectricUtility"] = ElectricUtilityInputs.info_dict(ElectricUtilityInputs) d["PV"] = PVInputs.info_dict(PVInputs) + d["CST"] = CSTInputs.info_dict(CSTInputs) d["ElectricStorage"] = ElectricStorageInputs.info_dict(ElectricStorageInputs) d["Wind"] = WindInputs.info_dict(WindInputs) d["Generator"] = GeneratorInputs.info_dict(GeneratorInputs) @@ -217,6 +218,9 @@ def results(request, run_uuid): try: r["inputs"]["Wind"] = meta.WindInputs.dict except: pass + try: r["inputs"]["CST"] = meta.CSTInputs.dict + except: pass + try: r["inputs"]["CoolingLoad"] = meta.CoolingLoadInputs.dict except: pass From d88e0dd8057e0320cdb5eb2ec1dab8f4ac7b9ac4 Mon Sep 17 00:00:00 2001 From: bill-becker Date: Tue, 6 May 2025 21:47:43 -0600 Subject: [PATCH 04/57] Merge migrations after merging develop --- reoptjl/migrations/0085_merge_20250507_0118.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 reoptjl/migrations/0085_merge_20250507_0118.py diff --git a/reoptjl/migrations/0085_merge_20250507_0118.py b/reoptjl/migrations/0085_merge_20250507_0118.py new file mode 100644 index 000000000..f192ab17b --- /dev/null +++ b/reoptjl/migrations/0085_merge_20250507_0118.py @@ -0,0 +1,14 @@ +# Generated by Django 4.0.7 on 2025-05-07 01:18 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('reoptjl', '0081_cstoutputs_cstinputs'), + ('reoptjl', '0084_merge_20250424_1814'), + ] + + operations = [ + ] From dd4f39088cd6416e262910287e0a3633311f8c55 Mon Sep 17 00:00:00 2001 From: bill-becker Date: Tue, 6 May 2025 21:48:52 -0600 Subject: [PATCH 05/57] Fix CSTInputs relationship to meta with OneToOne --- .../migrations/0086_alter_cstinputs_meta.py | 19 +++++++++++++++++++ reoptjl/models.py | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 reoptjl/migrations/0086_alter_cstinputs_meta.py diff --git a/reoptjl/migrations/0086_alter_cstinputs_meta.py b/reoptjl/migrations/0086_alter_cstinputs_meta.py new file mode 100644 index 000000000..6ab8e8741 --- /dev/null +++ b/reoptjl/migrations/0086_alter_cstinputs_meta.py @@ -0,0 +1,19 @@ +# Generated by Django 4.0.7 on 2025-05-07 03:39 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('reoptjl', '0085_merge_20250507_0118'), + ] + + operations = [ + migrations.AlterField( + model_name='cstinputs', + name='meta', + field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='CSTInputs', to='reoptjl.apimeta'), + ), + ] diff --git a/reoptjl/models.py b/reoptjl/models.py index 6cb9bc1e9..02a392b5f 100644 --- a/reoptjl/models.py +++ b/reoptjl/models.py @@ -3102,7 +3102,7 @@ class PVOutputs(BaseModel, models.Model): class CSTInputs(BaseModel, models.Model): key = "CST" - meta = models.ForeignKey( + meta = models.OneToOneField( to=APIMeta, on_delete=models.CASCADE, related_name="CSTInputs", From 4fdcd2bafdf814a73e20fe6128c9a70adfd79a6b Mon Sep 17 00:00:00 2001 From: bill-becker Date: Tue, 6 May 2025 21:54:03 -0600 Subject: [PATCH 06/57] Refactor CST Inputs/Outputs model references to end, add to places --- reoptjl/models.py | 410 +++++++++++++++++---------------- reoptjl/src/process_results.py | 9 +- reoptjl/validators.py | 6 +- reoptjl/views.py | 17 +- 4 files changed, 226 insertions(+), 216 deletions(-) diff --git a/reoptjl/models.py b/reoptjl/models.py index 02a392b5f..409d0be95 100644 --- a/reoptjl/models.py +++ b/reoptjl/models.py @@ -3100,210 +3100,6 @@ class PVOutputs(BaseModel, models.Model): default=list, blank=True ) -class CSTInputs(BaseModel, models.Model): - key = "CST" - meta = models.OneToOneField( - to=APIMeta, - on_delete=models.CASCADE, - related_name="CSTInputs", - unique=False - ) - - tech_type = models.TextField( - blank=True, - default="ptc", - help_text="Type of CST you want to implement into your system" - ) - min_kw = models.FloatField( - default=0, - validators=[ - MinValueValidator(0), - MaxValueValidator(1.0e9) - ], - blank=True, - help_text="Minimum CST size constraint for optimization." - ) - max_kw = models.FloatField( - default=1.0e9, - validators=[ - MinValueValidator(0), - MaxValueValidator(1.0e9) - ], - blank=True, - help_text="Maximum CST size constraint for optimization (upper bound on additional capacity beyond existing_kw). Set to zero to disable PV" - ) - acres_per_kw = models.FloatField( - default=0.006, - validators=[ - MinValueValidator(0), - MaxValueValidator(1.0e3) - ], - blank=True, - help_text="Power density for CST" - ) - installed_cost_per_kw = models.FloatField( - default=1200, - validators=[ - MinValueValidator(0), - MaxValueValidator(1.0e5) - ], - blank=True, - help_text="Installed CST cost in $/kW" - ) - om_cost_per_kw = models.FloatField( - default=18, - validators=[ - MinValueValidator(0), - MaxValueValidator(1.0e3) - ], - blank=True, - help_text="Annual CST operations and maintenance costs in $/kW" - ) - om_cost_per_kwh = models.FloatField( - default=18, - validators=[ - MinValueValidator(0), - MaxValueValidator(1.0e3) - ], - blank=True, - help_text="Annual CST operations and maintenance costs in $/kWh" - ) - macrs_option_years = models.IntegerField( - default=MACRS_YEARS_CHOICES.FIVE, - choices=MACRS_YEARS_CHOICES.choices, - blank=True, - help_text="Duration over which accelerated depreciation will occur. Set to zero to disable" - ) - macrs_bonus_fraction = models.FloatField( - default=0.6, - validators=[ - MinValueValidator(0), - MaxValueValidator(1) - ], - blank=True, - help_text="Percent of upfront project costs to depreciate in year one in addition to scheduled depreciation" - ) - capacity_factor_series = ArrayField( - models.FloatField( - blank=True - ), - default=list, blank=True, - help_text=("Optional user-defined capacity factors for CST.") - ) - elec_consumption_factor_series = ArrayField( - models.FloatField( - blank=True - ), - default=list, blank=True, - help_text=("Optional user-defined electricity consumption factors for CST.") - ) - can_supply_steam_turbine = models.BooleanField( - default=False, - null=True, - blank=True, - help_text="Boolean indicator if CST can supply steam to the steam turbine for electric production" - ) - can_serve_dhw = models.BooleanField( - default=True, - null=True, - blank=True, - help_text="Boolean indicator if CST can serve hot water load" - ) - can_serve_space_heating = models.BooleanField( - default=True, - null=True, - blank=True, - help_text="Boolean indicator if CST can serve space heating load" - ) - can_serve_process_heat = models.BooleanField( - default=True, - null=True, - blank=True, - help_text="Boolean indicator if CST can serve process heat load" - ) - charge_storage_only = models.BooleanField( - default=True, - null=True, - blank=True, - help_text="Boolean indicator if CST can only supply hot TES" - ) - emissions_factor_lb_CO2_per_mmbtu = models.FloatField( - validators=[ - MinValueValidator(0), - MaxValueValidator(1e4) - ], - blank=True, - null=True, - help_text="Pounds of CO2 emitted per MMBTU of fuel burned." - ) - emissions_factor_lb_NOx_per_mmbtu = models.FloatField( - validators=[ - MinValueValidator(0), - MaxValueValidator(1e4) - ], - blank=True, - null=True, - help_text="Pounds of CO2 emitted per MMBTU of fuel burned." - ) - emissions_factor_lb_SO2_per_mmbtu = models.FloatField( - validators=[ - MinValueValidator(0), - MaxValueValidator(1e4) - ], - blank=True, - null=True, - help_text="Pounds of CO2 emitted per MMBTU of fuel burned." - ) - emissions_factor_lb_PM25_per_mmbtu = models.FloatField( - validators=[ - MinValueValidator(0), - MaxValueValidator(1e4) - ], - blank=True, - null=True, - help_text="Pounds of CO2 emitted per MMBTU of fuel burned." - ) - -class CSTOutputs(BaseModel, models.Model): - key = "CSTOutputs" - meta = models.ForeignKey( - to=APIMeta, - on_delete=models.CASCADE, - related_name="CSTOutputs", - unique=False - ) - size_kw = models.FloatField(null=True, blank=True) - annual_electric_consumption_kwh = models.FloatField(null=True, blank=True) - annual_thermal_production_mmbtu = models.FloatField(null=True, blank=True) - thermal_production_series_mmbtu_per_hour = ArrayField( - models.FloatField(null=True, blank=True), - default=list, blank=True - ) - electric_consumption_series_kw = ArrayField( - models.FloatField(null=True, blank=True), - default=list, blank=True - ) - thermal_to_storage_series_mmbtu_per_hour = ArrayField( - models.FloatField(null=True, blank=True), - blank=True, default=list - ) - thermal_to_hot_sensible_tes_storage_series_mmbtu_per_hour = ArrayField( - models.FloatField(null=True, blank=True), - blank=True, default=list - ) - thermal_to_steamturbine_series_mmbtu_per_hour = ArrayField( - models.FloatField(null=True, blank=True), - blank=True, default=list - ) - thermal_curtailed_series_mmbtu_per_hour = ArrayField( - models.FloatField(null=True, blank=True), - blank=True, default=list - ) - thermal_to_load_series_mmbtu_per_hour = ArrayField( - models.FloatField(null=True, blank=True), - default=list, blank=True - ) - class WindInputs(BaseModel, models.Model): key = "Wind" @@ -8634,6 +8430,212 @@ class GHPOutputs(BaseModel, models.Model): thermal_to_load_series_ton = ArrayField(models.FloatField(null=True, blank=True), default=list, null=True, blank=True) avoided_capex_by_ghp_present_value = models.FloatField(null=True, blank=True) + +class CSTInputs(BaseModel, models.Model): + key = "CST" + meta = models.OneToOneField( + to=APIMeta, + on_delete=models.CASCADE, + related_name="CSTInputs", + unique=False + ) + + tech_type = models.TextField( + blank=True, + default="ptc", + help_text="Type of CST you want to implement into your system" + ) + min_kw = models.FloatField( + default=0, + validators=[ + MinValueValidator(0), + MaxValueValidator(1.0e9) + ], + blank=True, + help_text="Minimum CST size constraint for optimization." + ) + max_kw = models.FloatField( + default=1.0e9, + validators=[ + MinValueValidator(0), + MaxValueValidator(1.0e9) + ], + blank=True, + help_text="Maximum CST size constraint for optimization (upper bound on additional capacity beyond existing_kw). Set to zero to disable PV" + ) + acres_per_kw = models.FloatField( + default=0.006, + validators=[ + MinValueValidator(0), + MaxValueValidator(1.0e3) + ], + blank=True, + help_text="Power density for CST" + ) + installed_cost_per_kw = models.FloatField( + default=1200, + validators=[ + MinValueValidator(0), + MaxValueValidator(1.0e5) + ], + blank=True, + help_text="Installed CST cost in $/kW" + ) + om_cost_per_kw = models.FloatField( + default=18, + validators=[ + MinValueValidator(0), + MaxValueValidator(1.0e3) + ], + blank=True, + help_text="Annual CST operations and maintenance costs in $/kW" + ) + om_cost_per_kwh = models.FloatField( + default=18, + validators=[ + MinValueValidator(0), + MaxValueValidator(1.0e3) + ], + blank=True, + help_text="Annual CST operations and maintenance costs in $/kWh" + ) + macrs_option_years = models.IntegerField( + default=MACRS_YEARS_CHOICES.FIVE, + choices=MACRS_YEARS_CHOICES.choices, + blank=True, + help_text="Duration over which accelerated depreciation will occur. Set to zero to disable" + ) + macrs_bonus_fraction = models.FloatField( + default=0.6, + validators=[ + MinValueValidator(0), + MaxValueValidator(1) + ], + blank=True, + help_text="Percent of upfront project costs to depreciate in year one in addition to scheduled depreciation" + ) + capacity_factor_series = ArrayField( + models.FloatField( + blank=True + ), + default=list, blank=True, + help_text=("Optional user-defined capacity factors for CST.") + ) + elec_consumption_factor_series = ArrayField( + models.FloatField( + blank=True + ), + default=list, blank=True, + help_text=("Optional user-defined electricity consumption factors for CST.") + ) + can_supply_steam_turbine = models.BooleanField( + default=False, + null=True, + blank=True, + help_text="Boolean indicator if CST can supply steam to the steam turbine for electric production" + ) + can_serve_dhw = models.BooleanField( + default=True, + null=True, + blank=True, + help_text="Boolean indicator if CST can serve hot water load" + ) + can_serve_space_heating = models.BooleanField( + default=True, + null=True, + blank=True, + help_text="Boolean indicator if CST can serve space heating load" + ) + can_serve_process_heat = models.BooleanField( + default=True, + null=True, + blank=True, + help_text="Boolean indicator if CST can serve process heat load" + ) + charge_storage_only = models.BooleanField( + default=True, + null=True, + blank=True, + help_text="Boolean indicator if CST can only supply hot TES" + ) + emissions_factor_lb_CO2_per_mmbtu = models.FloatField( + validators=[ + MinValueValidator(0), + MaxValueValidator(1e4) + ], + blank=True, + null=True, + help_text="Pounds of CO2 emitted per MMBTU of fuel burned." + ) + emissions_factor_lb_NOx_per_mmbtu = models.FloatField( + validators=[ + MinValueValidator(0), + MaxValueValidator(1e4) + ], + blank=True, + null=True, + help_text="Pounds of CO2 emitted per MMBTU of fuel burned." + ) + emissions_factor_lb_SO2_per_mmbtu = models.FloatField( + validators=[ + MinValueValidator(0), + MaxValueValidator(1e4) + ], + blank=True, + null=True, + help_text="Pounds of CO2 emitted per MMBTU of fuel burned." + ) + emissions_factor_lb_PM25_per_mmbtu = models.FloatField( + validators=[ + MinValueValidator(0), + MaxValueValidator(1e4) + ], + blank=True, + null=True, + help_text="Pounds of CO2 emitted per MMBTU of fuel burned." + ) + +class CSTOutputs(BaseModel, models.Model): + key = "CSTOutputs" + meta = models.ForeignKey( + to=APIMeta, + on_delete=models.CASCADE, + related_name="CSTOutputs", + unique=False + ) + size_kw = models.FloatField(null=True, blank=True) + annual_electric_consumption_kwh = models.FloatField(null=True, blank=True) + annual_thermal_production_mmbtu = models.FloatField(null=True, blank=True) + thermal_production_series_mmbtu_per_hour = ArrayField( + models.FloatField(null=True, blank=True), + default=list, blank=True + ) + electric_consumption_series_kw = ArrayField( + models.FloatField(null=True, blank=True), + default=list, blank=True + ) + thermal_to_storage_series_mmbtu_per_hour = ArrayField( + models.FloatField(null=True, blank=True), + blank=True, default=list + ) + thermal_to_hot_sensible_tes_storage_series_mmbtu_per_hour = ArrayField( + models.FloatField(null=True, blank=True), + blank=True, default=list + ) + thermal_to_steamturbine_series_mmbtu_per_hour = ArrayField( + models.FloatField(null=True, blank=True), + blank=True, default=list + ) + thermal_curtailed_series_mmbtu_per_hour = ArrayField( + models.FloatField(null=True, blank=True), + blank=True, default=list + ) + thermal_to_load_series_mmbtu_per_hour = ArrayField( + models.FloatField(null=True, blank=True), + default=list, blank=True + ) + + def get_input_dict_from_run_uuid(run_uuid:str): """ Construct the input dict for REopt.run_reopt diff --git a/reoptjl/src/process_results.py b/reoptjl/src/process_results.py index ae7a56f84..b10202113 100644 --- a/reoptjl/src/process_results.py +++ b/reoptjl/src/process_results.py @@ -8,7 +8,7 @@ REoptjlMessageOutputs, AbsorptionChillerOutputs, BoilerOutputs, SteamTurbineInputs, \ SteamTurbineOutputs, GHPInputs, GHPOutputs, ExistingChillerInputs, \ ElectricHeaterOutputs, ASHPSpaceHeaterOutputs, ASHPWaterHeaterOutputs, \ - SiteInputs, ASHPSpaceHeaterInputs, ASHPWaterHeaterInputs, CSTOutputs + SiteInputs, ASHPSpaceHeaterInputs, ASHPWaterHeaterInputs, CSTInputs, CSTOutputs import numpy as np import sys import traceback as tb @@ -48,8 +48,6 @@ def process_results(results: dict, run_uuid: str) -> None: GeneratorOutputs.create(meta=meta, **results["Generator"]).save() if "Wind" in results.keys(): WindOutputs.create(meta=meta, **results["Wind"]).save() - if "CST" in results.keys(): - CSTOutputs.create(meta=meta, **results["CST"]).save() if "Boiler" in results.keys(): BoilerOutputs.create(meta=meta, **results["Boiler"]).save() if "ExistingBoiler" in results.keys(): @@ -92,6 +90,8 @@ def process_results(results: dict, run_uuid: str) -> None: ASHPSpaceHeaterOutputs.create(meta=meta, **results["ASHPSpaceHeater"]).save() if "ASHPWaterHeater" in results.keys(): ASHPWaterHeaterOutputs.create(meta=meta, **results["ASHPWaterHeater"]).save() + if "CST" in results.keys(): + CSTOutputs.create(meta=meta, **results["CST"]).save() # TODO process rest of results except Exception as e: exc_type, exc_value, exc_traceback = sys.exc_info() @@ -148,6 +148,9 @@ def update_inputs_in_database(inputs_to_update: dict, run_uuid: str) -> None: if inputs_to_update["ASHPWaterHeater"]: prune_update_fields(ASHPWaterHeaterInputs, inputs_to_update["ASHPWaterHeater"]) ASHPWaterHeaterInputs.objects.filter(meta__run_uuid=run_uuid).update(**inputs_to_update["ASHPWaterHeater"]) + if inputs_to_update["CST"]: + prune_update_fields(CSTInputs, inputs_to_update["CST"]) + CSTInputs.objects.filter(meta__run_uuid=run_uuid).update(**inputs_to_update["CST"]) except Exception as e: exc_type, exc_value, exc_traceback = sys.exc_info() debug_msg = "exc_type: {}; exc_value: {}; exc_traceback: {}".format( diff --git a/reoptjl/validators.py b/reoptjl/validators.py index 84df48836..454e5c5d4 100644 --- a/reoptjl/validators.py +++ b/reoptjl/validators.py @@ -5,7 +5,7 @@ FinancialInputs, BaseModel, Message, ElectricUtilityInputs, PVInputs, CSTInputs, ElectricStorageInputs, GeneratorInputs, WindInputs, SpaceHeatingLoadInputs, \ DomesticHotWaterLoadInputs, CHPInputs, CoolingLoadInputs, ExistingChillerInputs, HotThermalStorageInputs, ColdThermalStorageInputs, \ AbsorptionChillerInputs, BoilerInputs, SteamTurbineInputs, GHPInputs, ProcessHeatLoadInputs, ElectricHeaterInputs, ASHPSpaceHeaterInputs, \ - ASHPWaterHeaterInputs + ASHPWaterHeaterInputs, CSTInputs from django.core.exceptions import ValidationError from pyproj import Proj from typing import Tuple @@ -66,7 +66,6 @@ def __init__(self, raw_inputs: dict, ghpghx_inputs_validation_errors=None): FinancialInputs, ElectricUtilityInputs, PVInputs, - CSTInputs, ElectricStorageInputs, GeneratorInputs, WindInputs, @@ -85,7 +84,8 @@ def __init__(self, raw_inputs: dict, ghpghx_inputs_validation_errors=None): ProcessHeatLoadInputs, ElectricHeaterInputs, ASHPSpaceHeaterInputs, - ASHPWaterHeaterInputs + ASHPWaterHeaterInputs, + CSTInputs ) self.pvnames = [] on_grid_required_object_names = [ diff --git a/reoptjl/views.py b/reoptjl/views.py index dcd888249..13a016811 100644 --- a/reoptjl/views.py +++ b/reoptjl/views.py @@ -8,7 +8,7 @@ import re from django.http import JsonResponse, HttpResponse from reo.exceptions import UnexpectedError -from reoptjl.models import Settings, PVInputs, CSTInputs, ElectricStorageInputs, WindInputs, GeneratorInputs, ElectricLoadInputs,\ +from reoptjl.models import Settings, PVInputs, ElectricStorageInputs, WindInputs, GeneratorInputs, ElectricLoadInputs,\ ElectricTariffInputs, ElectricUtilityInputs, SpaceHeatingLoadInputs, PVOutputs, ElectricStorageOutputs,\ WindOutputs, ExistingBoilerInputs, GeneratorOutputs, ElectricTariffOutputs, ElectricUtilityOutputs, \ ElectricLoadOutputs, ExistingBoilerOutputs, DomesticHotWaterLoadInputs, SiteInputs, SiteOutputs, APIMeta, \ @@ -17,7 +17,8 @@ ColdThermalStorageInputs, ColdThermalStorageOutputs, AbsorptionChillerInputs, AbsorptionChillerOutputs,\ FinancialInputs, FinancialOutputs, UserUnlinkedRuns, BoilerInputs, BoilerOutputs, SteamTurbineInputs, \ SteamTurbineOutputs, GHPInputs, GHPOutputs, ProcessHeatLoadInputs, ElectricHeaterInputs, ElectricHeaterOutputs, \ - ASHPSpaceHeaterInputs, ASHPSpaceHeaterOutputs, ASHPWaterHeaterInputs, ASHPWaterHeaterOutputs, PortfolioUnlinkedRuns + ASHPSpaceHeaterInputs, ASHPSpaceHeaterOutputs, ASHPWaterHeaterInputs, ASHPWaterHeaterOutputs, PortfolioUnlinkedRuns, \ + CSTInputs, CSTOutputs import os import requests @@ -62,7 +63,6 @@ def help(request): d["ElectricTariff"] = ElectricTariffInputs.info_dict(ElectricTariffInputs) d["ElectricUtility"] = ElectricUtilityInputs.info_dict(ElectricUtilityInputs) d["PV"] = PVInputs.info_dict(PVInputs) - d["CST"] = CSTInputs.info_dict(CSTInputs) d["ElectricStorage"] = ElectricStorageInputs.info_dict(ElectricStorageInputs) d["Wind"] = WindInputs.info_dict(WindInputs) d["Generator"] = GeneratorInputs.info_dict(GeneratorInputs) @@ -83,6 +83,7 @@ def help(request): d["ElectricHeater"] = ElectricHeaterInputs.info_dict(ElectricHeaterInputs) d["ASHPSpaceHeater"] = ASHPSpaceHeaterInputs.info_dict(ASHPSpaceHeaterInputs) d["ASHPWaterHeater"] = ASHPWaterHeaterInputs.info_dict(ASHPWaterHeaterInputs) + d["CST"] = CSTInputs.info_dict(CSTInputs) return JsonResponse(d) @@ -134,6 +135,8 @@ def outputs(request): d["ASHPWaterHeater"] = ASHPWaterHeaterOutputs.info_dict(ASHPWaterHeaterOutputs) d["Messages"] = REoptjlMessageOutputs.info_dict(REoptjlMessageOutputs) d["SteamTurbine"] = SteamTurbineOutputs.info_dict(SteamTurbineOutputs) + d["CST"] = CSTOutputs.info_dict(CSTOutputs) + return JsonResponse(d) except Exception as e: @@ -218,9 +221,6 @@ def results(request, run_uuid): try: r["inputs"]["Wind"] = meta.WindInputs.dict except: pass - try: r["inputs"]["CST"] = meta.CSTInputs.dict - except: pass - try: r["inputs"]["CoolingLoad"] = meta.CoolingLoadInputs.dict except: pass @@ -269,6 +269,9 @@ def results(request, run_uuid): try: r["inputs"]["ASHPWaterHeater"] = meta.ASHPWaterHeaterInputs.dict except: pass + try: r["inputs"]["CST"] = meta.CSTInputs.dict + except: pass + try: r["outputs"] = dict() r["messages"] = dict() @@ -347,6 +350,8 @@ def results(request, run_uuid): except: pass try: r["outputs"]["ASHPWaterHeater"] = meta.ASHPWaterHeaterOutputs.dict except: pass + try: r["outputs"]["CST"] = meta.CSTOutputs.dict + except: pass for d in r["outputs"].values(): if isinstance(d, dict): From b36efd5fcc7296349e8923bb0cf47bf750e2de11 Mon Sep 17 00:00:00 2001 From: bill-becker Date: Wed, 7 May 2025 11:16:57 -0600 Subject: [PATCH 07/57] Add ENV for NREL_DEVELOPER_EMAIL in http.jl Needed for NSRDB API call within SAM SSC for CST --- julia_src/http.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/julia_src/http.jl b/julia_src/http.jl index ebbfe6ea2..ede6e8b72 100644 --- a/julia_src/http.jl +++ b/julia_src/http.jl @@ -7,6 +7,8 @@ DotEnv.load!() const test_nrel_developer_api_key = ENV["NREL_DEVELOPER_API_KEY"] +ENV["NREL_DEVELOPER_EMAIL"] = "reopt@nrel.gov" + include("os_solvers.jl") # Load Xpress only if it is installed, as indicated by ENV["XPRESS_INSTALLED"]="True" From e0d4a119be6c16498dd881883e26f5463fe84602 Mon Sep 17 00:00:00 2001 From: Kadlec Date: Wed, 7 May 2025 13:07:51 -0600 Subject: [PATCH 08/57] Adding in temperature inputs to CSTInputs object --- ...et_temp_degf_cstinputs_outlet_temp_degf.py | 24 +++++++++++++++++++ reoptjl/models.py | 18 ++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 reoptjl/migrations/0087_cstinputs_inlet_temp_degf_cstinputs_outlet_temp_degf.py diff --git a/reoptjl/migrations/0087_cstinputs_inlet_temp_degf_cstinputs_outlet_temp_degf.py b/reoptjl/migrations/0087_cstinputs_inlet_temp_degf_cstinputs_outlet_temp_degf.py new file mode 100644 index 000000000..f2ac2ed53 --- /dev/null +++ b/reoptjl/migrations/0087_cstinputs_inlet_temp_degf_cstinputs_outlet_temp_degf.py @@ -0,0 +1,24 @@ +# Generated by Django 4.0.7 on 2025-05-07 18:54 + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('reoptjl', '0086_alter_cstinputs_meta'), + ] + + operations = [ + migrations.AddField( + model_name='cstinputs', + name='inlet_temp_degF', + field=models.FloatField(blank=True, default=500, help_text="This is the temperature at which your process needs the heat transfer fluid specified above to be at when entering your facility. In other words, this is your 'hot' temperature.", validators=[django.core.validators.MinValueValidator(300), django.core.validators.MaxValueValidator(750)]), + ), + migrations.AddField( + model_name='cstinputs', + name='outlet_temp_degF', + field=models.FloatField(blank=True, default=400, help_text="This is the temperature at which your the heat transfer fluid specified above returns from your process after heat has been extracted. In other words, this is your cold' temperature. If you have an open system, the inlet temperature will be assumed to be ambient temperature (20 C / 68 F).", validators=[django.core.validators.MinValueValidator(300), django.core.validators.MaxValueValidator(750)]), + ), + ] diff --git a/reoptjl/models.py b/reoptjl/models.py index 409d0be95..e39babadc 100644 --- a/reoptjl/models.py +++ b/reoptjl/models.py @@ -8463,6 +8463,24 @@ class CSTInputs(BaseModel, models.Model): blank=True, help_text="Maximum CST size constraint for optimization (upper bound on additional capacity beyond existing_kw). Set to zero to disable PV" ) + inlet_temp_degF = models.FloatField( + default=500, + validators=[ + MinValueValidator(300), + MaxValueValidator(750) + ], + blank=True, + help_text="This is the temperature at which your process needs the heat transfer fluid specified above to be at when entering your facility. In other words, this is your 'hot' temperature." + ) + outlet_temp_degF = models.FloatField( + default=400, + validators=[ + MinValueValidator(300), + MaxValueValidator(750) + ], + blank=True, + help_text="This is the temperature at which your the heat transfer fluid specified above returns from your process after heat has been extracted. In other words, this is your cold' temperature. If you have an open system, the inlet temperature will be assumed to be ambient temperature (20 C / 68 F)." + ) acres_per_kw = models.FloatField( default=0.006, validators=[ From 423bcc7f34d79d4da811f8bd11e23ac39c223647 Mon Sep 17 00:00:00 2001 From: Kadlec Date: Thu, 8 May 2025 11:38:30 -0600 Subject: [PATCH 09/57] adding in API outputs for CST --- ...8_alter_cstinputs_acres_per_kw_and_more.py | 39 +++++++++++++++++++ ..._hot_sensible_tes_series_mmbtu_per_hour.py | 19 +++++++++ ..._hot_sensible_tes_series_mmbtu_per_hour.py | 19 +++++++++ ...dhw_load_series_mmbtu_per_hour_and_more.py | 29 ++++++++++++++ reoptjl/models.py | 31 ++++++++++++--- 5 files changed, 132 insertions(+), 5 deletions(-) create mode 100644 reoptjl/migrations/0088_alter_cstinputs_acres_per_kw_and_more.py create mode 100644 reoptjl/migrations/0089_electricheateroutputs_thermal_to_hot_sensible_tes_series_mmbtu_per_hour.py create mode 100644 reoptjl/migrations/0090_cstoutputs_thermal_to_hot_sensible_tes_series_mmbtu_per_hour.py create mode 100644 reoptjl/migrations/0091_cstoutputs_thermal_to_dhw_load_series_mmbtu_per_hour_and_more.py diff --git a/reoptjl/migrations/0088_alter_cstinputs_acres_per_kw_and_more.py b/reoptjl/migrations/0088_alter_cstinputs_acres_per_kw_and_more.py new file mode 100644 index 000000000..62cff09b5 --- /dev/null +++ b/reoptjl/migrations/0088_alter_cstinputs_acres_per_kw_and_more.py @@ -0,0 +1,39 @@ +# Generated by Django 4.0.7 on 2025-05-08 16:45 + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('reoptjl', '0087_cstinputs_inlet_temp_degf_cstinputs_outlet_temp_degf'), + ] + + operations = [ + migrations.AlterField( + model_name='cstinputs', + name='acres_per_kw', + field=models.FloatField(blank=True, default=0.000939, help_text='Power density for CST', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1000.0)]), + ), + migrations.AlterField( + model_name='cstinputs', + name='charge_storage_only', + field=models.BooleanField(blank=True, default=False, help_text='Boolean indicator if CST can only supply hot TES', null=True), + ), + migrations.AlterField( + model_name='cstinputs', + name='macrs_bonus_fraction', + field=models.FloatField(blank=True, default=0.0, help_text='Percent of upfront project costs to depreciate in year one in addition to scheduled depreciation', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1)]), + ), + migrations.AlterField( + model_name='cstinputs', + name='om_cost_per_kw', + field=models.FloatField(blank=True, default=0.0, help_text='Annual CST operations and maintenance costs in $/kW', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1000.0)]), + ), + migrations.AlterField( + model_name='cstinputs', + name='om_cost_per_kwh', + field=models.FloatField(blank=True, default=0.0, help_text='Annual CST operations and maintenance costs in $/kWh', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1000.0)]), + ), + ] diff --git a/reoptjl/migrations/0089_electricheateroutputs_thermal_to_hot_sensible_tes_series_mmbtu_per_hour.py b/reoptjl/migrations/0089_electricheateroutputs_thermal_to_hot_sensible_tes_series_mmbtu_per_hour.py new file mode 100644 index 000000000..768c14681 --- /dev/null +++ b/reoptjl/migrations/0089_electricheateroutputs_thermal_to_hot_sensible_tes_series_mmbtu_per_hour.py @@ -0,0 +1,19 @@ +# Generated by Django 4.0.7 on 2025-05-08 16:56 + +import django.contrib.postgres.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('reoptjl', '0088_alter_cstinputs_acres_per_kw_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='electricheateroutputs', + name='thermal_to_hot_sensible_tes_series_mmbtu_per_hour', + field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), default=list, size=None), + ), + ] diff --git a/reoptjl/migrations/0090_cstoutputs_thermal_to_hot_sensible_tes_series_mmbtu_per_hour.py b/reoptjl/migrations/0090_cstoutputs_thermal_to_hot_sensible_tes_series_mmbtu_per_hour.py new file mode 100644 index 000000000..09ab4516d --- /dev/null +++ b/reoptjl/migrations/0090_cstoutputs_thermal_to_hot_sensible_tes_series_mmbtu_per_hour.py @@ -0,0 +1,19 @@ +# Generated by Django 4.0.7 on 2025-05-08 17:08 + +import django.contrib.postgres.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('reoptjl', '0089_electricheateroutputs_thermal_to_hot_sensible_tes_series_mmbtu_per_hour'), + ] + + operations = [ + migrations.AddField( + model_name='cstoutputs', + name='thermal_to_hot_sensible_tes_series_mmbtu_per_hour', + field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), blank=True, default=list, size=None), + ), + ] diff --git a/reoptjl/migrations/0091_cstoutputs_thermal_to_dhw_load_series_mmbtu_per_hour_and_more.py b/reoptjl/migrations/0091_cstoutputs_thermal_to_dhw_load_series_mmbtu_per_hour_and_more.py new file mode 100644 index 000000000..403067fff --- /dev/null +++ b/reoptjl/migrations/0091_cstoutputs_thermal_to_dhw_load_series_mmbtu_per_hour_and_more.py @@ -0,0 +1,29 @@ +# Generated by Django 4.0.7 on 2025-05-08 17:23 + +import django.contrib.postgres.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('reoptjl', '0090_cstoutputs_thermal_to_hot_sensible_tes_series_mmbtu_per_hour'), + ] + + operations = [ + migrations.AddField( + model_name='cstoutputs', + name='thermal_to_dhw_load_series_mmbtu_per_hour', + field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), blank=True, default=list, size=None), + ), + migrations.AddField( + model_name='cstoutputs', + name='thermal_to_process_heat_load_series_mmbtu_per_hour', + field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), default=list, size=None), + ), + migrations.AddField( + model_name='cstoutputs', + name='thermal_to_space_heating_load_series_mmbtu_per_hour', + field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), default=list, size=None), + ), + ] diff --git a/reoptjl/models.py b/reoptjl/models.py index e39babadc..1c0f39108 100644 --- a/reoptjl/models.py +++ b/reoptjl/models.py @@ -5491,6 +5491,11 @@ class ElectricHeaterOutputs(BaseModel, models.Model): default = list ) + thermal_to_hot_sensible_tes_series_mmbtu_per_hour = ArrayField( + models.FloatField(null=True, blank=True), + default = list + ) + class ASHPSpaceHeaterInputs(BaseModel, models.Model): key = "ASHPSpaceHeater" meta = models.OneToOneField( @@ -8482,7 +8487,7 @@ class CSTInputs(BaseModel, models.Model): help_text="This is the temperature at which your the heat transfer fluid specified above returns from your process after heat has been extracted. In other words, this is your cold' temperature. If you have an open system, the inlet temperature will be assumed to be ambient temperature (20 C / 68 F)." ) acres_per_kw = models.FloatField( - default=0.006, + default=0.000939, validators=[ MinValueValidator(0), MaxValueValidator(1.0e3) @@ -8500,7 +8505,7 @@ class CSTInputs(BaseModel, models.Model): help_text="Installed CST cost in $/kW" ) om_cost_per_kw = models.FloatField( - default=18, + default=0.0, validators=[ MinValueValidator(0), MaxValueValidator(1.0e3) @@ -8509,7 +8514,7 @@ class CSTInputs(BaseModel, models.Model): help_text="Annual CST operations and maintenance costs in $/kW" ) om_cost_per_kwh = models.FloatField( - default=18, + default=0.0, validators=[ MinValueValidator(0), MaxValueValidator(1.0e3) @@ -8524,7 +8529,7 @@ class CSTInputs(BaseModel, models.Model): help_text="Duration over which accelerated depreciation will occur. Set to zero to disable" ) macrs_bonus_fraction = models.FloatField( - default=0.6, + default=0.0, validators=[ MinValueValidator(0), MaxValueValidator(1) @@ -8571,7 +8576,7 @@ class CSTInputs(BaseModel, models.Model): help_text="Boolean indicator if CST can serve process heat load" ) charge_storage_only = models.BooleanField( - default=True, + default=False, null=True, blank=True, help_text="Boolean indicator if CST can only supply hot TES" @@ -8640,6 +8645,10 @@ class CSTOutputs(BaseModel, models.Model): models.FloatField(null=True, blank=True), blank=True, default=list ) + thermal_to_hot_sensible_tes_series_mmbtu_per_hour = ArrayField( + models.FloatField(null=True, blank=True), + blank=True, default=list + ) thermal_to_steamturbine_series_mmbtu_per_hour = ArrayField( models.FloatField(null=True, blank=True), blank=True, default=list @@ -8652,6 +8661,18 @@ class CSTOutputs(BaseModel, models.Model): models.FloatField(null=True, blank=True), default=list, blank=True ) + thermal_to_dhw_load_series_mmbtu_per_hour = ArrayField( + models.FloatField(null=True, blank=True), + default=list, blank=True + ) + thermal_to_space_heating_load_series_mmbtu_per_hour = ArrayField( + models.FloatField(null=True, blank=True), + default = list + ) + thermal_to_process_heat_load_series_mmbtu_per_hour = ArrayField( + models.FloatField(null=True, blank=True), + default = list + ) def get_input_dict_from_run_uuid(run_uuid:str): From 025fcb36a4cdadfd5a94dacbe2c207409d4afd31 Mon Sep 17 00:00:00 2001 From: bill-becker Date: Thu, 8 May 2025 12:40:43 -0600 Subject: [PATCH 10/57] Change ForeignKey to OneToOne for CSTOutputs --- .../migrations/0092_alter_cstoutputs_meta.py | 19 +++++++++++++++++++ reoptjl/models.py | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 reoptjl/migrations/0092_alter_cstoutputs_meta.py diff --git a/reoptjl/migrations/0092_alter_cstoutputs_meta.py b/reoptjl/migrations/0092_alter_cstoutputs_meta.py new file mode 100644 index 000000000..d846fccb6 --- /dev/null +++ b/reoptjl/migrations/0092_alter_cstoutputs_meta.py @@ -0,0 +1,19 @@ +# Generated by Django 4.0.7 on 2025-05-08 18:28 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('reoptjl', '0091_cstoutputs_thermal_to_dhw_load_series_mmbtu_per_hour_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='cstoutputs', + name='meta', + field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='CSTOutputs', to='reoptjl.apimeta'), + ), + ] diff --git a/reoptjl/models.py b/reoptjl/models.py index 1c0f39108..78eed837f 100644 --- a/reoptjl/models.py +++ b/reoptjl/models.py @@ -8620,7 +8620,7 @@ class CSTInputs(BaseModel, models.Model): class CSTOutputs(BaseModel, models.Model): key = "CSTOutputs" - meta = models.ForeignKey( + meta = models.OneToOneField( to=APIMeta, on_delete=models.CASCADE, related_name="CSTOutputs", From 97bb73fa2afb591be3bf72a12735bad7cb55f63f Mon Sep 17 00:00:00 2001 From: Kadlec Date: Fri, 9 May 2025 11:37:34 -0600 Subject: [PATCH 11/57] adding HotSensibleTES to API --- ...esinputs_hotsensibletesoutputs_and_more.py | 68 ++++++ reoptjl/models.py | 225 ++++++++++++++++++ reoptjl/validators.py | 3 +- reoptjl/views.py | 9 +- 4 files changed, 303 insertions(+), 2 deletions(-) create mode 100644 reoptjl/migrations/0093_hotsensibletesinputs_hotsensibletesoutputs_and_more.py diff --git a/reoptjl/migrations/0093_hotsensibletesinputs_hotsensibletesoutputs_and_more.py b/reoptjl/migrations/0093_hotsensibletesinputs_hotsensibletesoutputs_and_more.py new file mode 100644 index 000000000..04fcf6c17 --- /dev/null +++ b/reoptjl/migrations/0093_hotsensibletesinputs_hotsensibletesoutputs_and_more.py @@ -0,0 +1,68 @@ +# Generated by Django 4.0.7 on 2025-05-09 17:25 + +import django.contrib.postgres.fields +import django.core.validators +from django.db import migrations, models +import django.db.models.deletion +import reoptjl.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('reoptjl', '0092_alter_cstoutputs_meta'), + ] + + operations = [ + migrations.CreateModel( + name='HotSensibleTESInputs', + fields=[ + ('meta', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, related_name='HotSensibleTESInputs', serialize=False, to='reoptjl.apimeta')), + ('min_kwh', models.FloatField(blank=True, default=0.0, help_text='Minimum TES volume (energy) size constraint for optimization', null=True, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1000000000.0)])), + ('max_kwh', models.FloatField(blank=True, default=0.0, help_text='Maximum TES volume (energy) size constraint for optimization. Set to zero to disable storage', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1000000000.0)])), + ('hot_temp_degF', models.FloatField(blank=True, default=1065.0, help_text='Hot-side supply water temperature from HotTES (top of tank) to the heating load', validators=[django.core.validators.MinValueValidator(200.0), django.core.validators.MaxValueValidator(2000.0)])), + ('cool_temp_degF', models.FloatField(blank=True, default=554.0, help_text='Cold-side return water temperature from the heating load to the HotTES (bottom of tank)', validators=[django.core.validators.MinValueValidator(200.0), django.core.validators.MaxValueValidator(2000.0)])), + ('internal_efficiency_fraction', models.FloatField(blank=True, default=0.999999, help_text='Thermal losses due to mixing from thermal power entering or leaving tank', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1.0)])), + ('soc_min_fraction', models.FloatField(blank=True, default=0.1, help_text='Minimum allowable battery state of charge as fraction of energy capacity.', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1.0)])), + ('soc_init_fraction', models.FloatField(blank=True, default=0.5, help_text='Battery state of charge at first hour of optimization as fraction of energy capacity.', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1.0)])), + ('installed_cost_per_kwh', models.FloatField(blank=True, default=1.5, help_text='Installed hot TES cost in $/kwh', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(10000.0)])), + ('om_cost_per_kwh', models.FloatField(blank=True, default=0.0, help_text='Annual hot TES operations and maintenance costs in $/kwh', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1000.0)])), + ('thermal_decay_rate_fraction', models.FloatField(blank=True, default=0.0004, help_text='Thermal energy-based cost of TES (e.g. volume of the tank)', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1.0)])), + ('macrs_option_years', models.IntegerField(blank=True, choices=[(0, 'Zero'), (5, 'Five'), (7, 'Seven')], default=7, help_text='Duration over which accelerated depreciation will occur. Set to zero to disable')), + ('macrs_bonus_fraction', models.FloatField(blank=True, default=0.6, help_text='Percent of upfront project costs to depreciate in year one in addition to scheduled depreciation', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1)])), + ('macrs_itc_reduction', models.FloatField(blank=True, default=0.0, help_text='Percent of the ITC value by which depreciable basis is reduced', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1)])), + ('total_itc_fraction', models.FloatField(blank=True, default=0.3, help_text='Total investment tax credit in percent applied toward capital costs', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1)])), + ('total_rebate_per_kwh', models.FloatField(blank=True, default=0.0, help_text='Rebate per unit installed energy capacity', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1000000000.0)])), + ('can_serve_dhw', models.BooleanField(blank=True, default=False, help_text='Boolean indicator if hot thermal storage can serve space heating load', null=True)), + ('can_serve_space_heating', models.BooleanField(blank=True, default=False, help_text='Boolean indicator if hot thermal storage can serve space heating load', null=True)), + ('can_serve_process_heat', models.BooleanField(blank=True, default=True, help_text='Boolean indicator if hot thermal storage can serve process heat load', null=True)), + ('supply_turbine_only', models.BooleanField(blank=True, default=False, help_text='Boolean indicator if hot thermal storage can serve only steam turbine', null=True)), + ('one_direction_flow', models.BooleanField(blank=True, default=False, help_text='Boolean indicator if hot thermal storage can only', null=True)), + ('num_charge_hours', models.FloatField(blank=True, default=4.0, help_text='Number of charge hours', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(10000.0)])), + ('num_discharge_hours', models.FloatField(blank=True, default=10.0, help_text='Number of charge hours', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(10000.0)])), + ], + bases=(reoptjl.models.BaseModel, models.Model), + ), + migrations.CreateModel( + name='HotSensibleTESOutputs', + fields=[ + ('meta', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, related_name='HotSensibleTESOutputs', serialize=False, to='reoptjl.apimeta')), + ('size_gal', models.FloatField(blank=True, null=True)), + ('size_kwh', models.FloatField(blank=True, null=True)), + ('soc_series_fraction', django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), default=list, size=None)), + ('storage_to_load_series_mmbtu_per_hour', django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), default=list, size=None)), + ('storage_to_turbine_series_mmbtu_per_hour', django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), default=list, size=None)), + ], + bases=(reoptjl.models.BaseModel, models.Model), + ), + migrations.AddField( + model_name='hotthermalstorageoutputs', + name='size_kwh', + field=models.FloatField(blank=True, null=True), + ), + migrations.AddField( + model_name='hotthermalstorageoutputs', + name='storage_to_turbine_series_mmbtu_per_hour', + field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), default=list, size=None), + ), + ] diff --git a/reoptjl/models.py b/reoptjl/models.py index 78eed837f..e31b38771 100644 --- a/reoptjl/models.py +++ b/reoptjl/models.py @@ -6848,6 +6848,7 @@ class HotThermalStorageOutputs(BaseModel, models.Model): primary_key=True ) size_gal = models.FloatField(null=True, blank=True) + size_kwh = models.FloatField(null=True, blank=True) soc_series_fraction = ArrayField( models.FloatField(null=True, blank=True), default = list, @@ -6870,11 +6871,232 @@ class HotThermalStorageOutputs(BaseModel, models.Model): models.FloatField(null=True, blank=True), default = list ) + storage_to_turbine_series_mmbtu_per_hour = ArrayField( + models.FloatField(null=True, blank=True), + default = list + ) def clean(self): # perform custom validation here. pass +class HotSensibleTESInputs(BaseModel, models.Model): + key = "HotSensibleTES" + + meta = models.OneToOneField( + APIMeta, + on_delete=models.CASCADE, + related_name="HotSensibleTESInputs", + primary_key=True + ) + + min_kwh = models.FloatField( + validators=[ + MinValueValidator(0), + MaxValueValidator(1.0e9) + ], + null=True, + blank=True, + default=0.0, + help_text="Minimum TES volume (energy) size constraint for optimization" + ) + max_kwh = models.FloatField( + validators=[ + MinValueValidator(0), + MaxValueValidator(1.0e9) + ], + blank=True, + default=0.0, + help_text="Maximum TES volume (energy) size constraint for optimization. Set to zero to disable storage" + ) + hot_temp_degF = models.FloatField( + validators=[ + MinValueValidator(200.0), + MaxValueValidator(2000.0) + ], + blank=True, + default=1065.0, + help_text="Hot-side supply water temperature from HotTES (top of tank) to the heating load" + ) + cool_temp_degF = models.FloatField( + validators=[ + MinValueValidator(200.0), + MaxValueValidator(2000.0) + ], + blank=True, + default=554.0, + help_text="Cold-side return water temperature from the heating load to the HotTES (bottom of tank)" + ) + internal_efficiency_fraction = models.FloatField( + validators=[ + MinValueValidator(0), + MaxValueValidator(1.0) + ], + blank=True, + default=0.999999, + help_text="Thermal losses due to mixing from thermal power entering or leaving tank" + ) + soc_min_fraction = models.FloatField( + default=0.1, + validators=[ + MinValueValidator(0), + MaxValueValidator(1.0) + ], + blank=True, + help_text="Minimum allowable battery state of charge as fraction of energy capacity." + ) + soc_init_fraction = models.FloatField( + validators=[ + MinValueValidator(0), + MaxValueValidator(1.0) + ], + default=0.5, + blank=True, + help_text="Battery state of charge at first hour of optimization as fraction of energy capacity." + ) + installed_cost_per_kwh = models.FloatField( + default=1.5, + validators=[ + MinValueValidator(0), + MaxValueValidator(1.0e4) + ], + blank=True, + help_text="Installed hot TES cost in $/kwh" + ) + om_cost_per_kwh = models.FloatField( + default=0.0, + validators=[ + MinValueValidator(0), + MaxValueValidator(1.0e3) + ], + blank=True, + help_text="Annual hot TES operations and maintenance costs in $/kwh" + ) + thermal_decay_rate_fraction = models.FloatField( + default=0.0004, + validators=[ + MinValueValidator(0), + MaxValueValidator(1.0) + ], + blank=True, + help_text="Thermal energy-based cost of TES (e.g. volume of the tank)" + ) + macrs_option_years = models.IntegerField( + default=MACRS_YEARS_CHOICES.SEVEN, + choices=MACRS_YEARS_CHOICES.choices, + blank=True, + help_text="Duration over which accelerated depreciation will occur. Set to zero to disable" + ) + macrs_bonus_fraction = models.FloatField( + default=0.6, + validators=[ + MinValueValidator(0), + MaxValueValidator(1) + ], + blank=True, + help_text="Percent of upfront project costs to depreciate in year one in addition to scheduled depreciation" + ) + macrs_itc_reduction = models.FloatField( + default=0.0, + validators=[ + MinValueValidator(0), + MaxValueValidator(1) + ], + blank=True, + help_text="Percent of the ITC value by which depreciable basis is reduced" + ) + total_itc_fraction = models.FloatField( + default=0.3, + validators=[ + MinValueValidator(0), + MaxValueValidator(1) + ], + blank=True, + help_text="Total investment tax credit in percent applied toward capital costs" + ) + total_rebate_per_kwh = models.FloatField( + default=0.0, + validators=[ + MinValueValidator(0), + MaxValueValidator(1.0e9) + ], + blank=True, + help_text="Rebate per unit installed energy capacity" + ) + can_serve_dhw = models.BooleanField( + default=False, + null=True, + blank=True, + help_text="Boolean indicator if hot thermal storage can serve space heating load" + ) + can_serve_space_heating = models.BooleanField( + default=False, + null=True, + blank=True, + help_text="Boolean indicator if hot thermal storage can serve space heating load" + ) + can_serve_process_heat = models.BooleanField( + default=True, + null=True, + blank=True, + help_text="Boolean indicator if hot thermal storage can serve process heat load" + ) + supply_turbine_only = models.BooleanField( + default=False, + null=True, + blank=True, + help_text="Boolean indicator if hot thermal storage can serve only steam turbine" + ) + one_direction_flow = models.BooleanField( + default=False, + null=True, + blank=True, + help_text="Boolean indicator if hot thermal storage can only" + ) + num_charge_hours = models.FloatField( + default=4.0, + validators=[ + MinValueValidator(0), + MaxValueValidator(1e4) + ], + blank=True, + help_text="Number of charge hours" + ) + num_discharge_hours = models.FloatField( + default=10.0, + validators=[ + MinValueValidator(0), + MaxValueValidator(1e4) + ], + blank=True, + help_text="Number of charge hours" + ) + +class HotSensibleTESOutputs(BaseModel, models.Model): + key = "HotSensibleTESOutputs" + + meta = models.OneToOneField( + APIMeta, + on_delete=models.CASCADE, + related_name="HotSensibleTESOutputs", + primary_key=True + ) + size_gal = models.FloatField(null=True, blank=True) + size_kwh = models.FloatField(null=True, blank=True) + soc_series_fraction = ArrayField( + models.FloatField(null=True, blank=True), + default = list, + ) + storage_to_load_series_mmbtu_per_hour = ArrayField( + models.FloatField(null=True, blank=True), + default = list, + ) + storage_to_turbine_series_mmbtu_per_hour = ArrayField( + models.FloatField(null=True, blank=True), + default = list + ) + + class ColdThermalStorageInputs(BaseModel, models.Model): key = "ColdThermalStorage" @@ -8749,6 +8971,9 @@ def filter_none_and_empty_array(d:dict): try: d["HotThermalStorage"] = filter_none_and_empty_array(meta.HotThermalStorageInputs.dict) except: pass + try: d["HotSensibleTES"] = filter_none_and_empty_array(meta.HotSensibleTESInputs.dict) + except: pass + try: d["ColdThermalStorage"] = filter_none_and_empty_array(meta.ColdThermalStorageInputs.dict) except: pass diff --git a/reoptjl/validators.py b/reoptjl/validators.py index 454e5c5d4..22cb082af 100644 --- a/reoptjl/validators.py +++ b/reoptjl/validators.py @@ -5,7 +5,7 @@ FinancialInputs, BaseModel, Message, ElectricUtilityInputs, PVInputs, CSTInputs, ElectricStorageInputs, GeneratorInputs, WindInputs, SpaceHeatingLoadInputs, \ DomesticHotWaterLoadInputs, CHPInputs, CoolingLoadInputs, ExistingChillerInputs, HotThermalStorageInputs, ColdThermalStorageInputs, \ AbsorptionChillerInputs, BoilerInputs, SteamTurbineInputs, GHPInputs, ProcessHeatLoadInputs, ElectricHeaterInputs, ASHPSpaceHeaterInputs, \ - ASHPWaterHeaterInputs, CSTInputs + ASHPWaterHeaterInputs, CSTInputs, HotSensibleTESInputs from django.core.exceptions import ValidationError from pyproj import Proj from typing import Tuple @@ -77,6 +77,7 @@ def __init__(self, raw_inputs: dict, ghpghx_inputs_validation_errors=None): CHPInputs, BoilerInputs, HotThermalStorageInputs, + HotSensibleTESInputs, ColdThermalStorageInputs, AbsorptionChillerInputs, SteamTurbineInputs, diff --git a/reoptjl/views.py b/reoptjl/views.py index 13a016811..84c3c80de 100644 --- a/reoptjl/views.py +++ b/reoptjl/views.py @@ -18,7 +18,7 @@ FinancialInputs, FinancialOutputs, UserUnlinkedRuns, BoilerInputs, BoilerOutputs, SteamTurbineInputs, \ SteamTurbineOutputs, GHPInputs, GHPOutputs, ProcessHeatLoadInputs, ElectricHeaterInputs, ElectricHeaterOutputs, \ ASHPSpaceHeaterInputs, ASHPSpaceHeaterOutputs, ASHPWaterHeaterInputs, ASHPWaterHeaterOutputs, PortfolioUnlinkedRuns, \ - CSTInputs, CSTOutputs + CSTInputs, CSTOutputs, HotSensibleTESInputs, HotSensibleTESOutputs import os import requests @@ -71,6 +71,7 @@ def help(request): d["ExistingBoiler"] = ExistingBoilerInputs.info_dict(ExistingBoilerInputs) d["Boiler"] = BoilerInputs.info_dict(BoilerInputs) d["HotThermalStorage"] = HotThermalStorageInputs.info_dict(HotThermalStorageInputs) + d["HotSensibleTES"] = HotSensibleTESInputs.info_dict(HotSensibleTESInputs) d["ColdThermalStorage"] = ColdThermalStorageInputs.info_dict(ColdThermalStorageInputs) d["SpaceHeatingLoad"] = SpaceHeatingLoadInputs.info_dict(SpaceHeatingLoadInputs) d["DomesticHotWaterLoad"] = DomesticHotWaterLoadInputs.info_dict(DomesticHotWaterLoadInputs) @@ -123,6 +124,7 @@ def outputs(request): d["ExistingBoiler"] = ExistingBoilerOutputs.info_dict(ExistingBoilerOutputs) d["Boiler"] = BoilerOutputs.info_dict(BoilerOutputs) d["HotThermalStorage"] = HotThermalStorageOutputs.info_dict(HotThermalStorageOutputs) + d["HotSensibleTES"] = HotSensibleTESOutputs.info_dict(HotSensibleTESOutputs) d["ColdThermalStorage"] = ColdThermalStorageOutputs.info_dict(ColdThermalStorageOutputs) d["Site"] = SiteOutputs.info_dict(SiteOutputs) d["HeatingLoad"] = HeatingLoadOutputs.info_dict(HeatingLoadOutputs) @@ -236,6 +238,9 @@ def results(request, run_uuid): try: r["inputs"]["HotThermalStorage"] = meta.HotThermalStorageInputs.dict except: pass + try: r["inputs"]["HotSensibleTES"] = meta.HotSensibleTESInputs.dict + except: pass + try: r["inputs"]["ColdThermalStorage"] = meta.ColdThermalStorageInputs.dict except: pass @@ -330,6 +335,8 @@ def results(request, run_uuid): try: r["outputs"]["HotThermalStorage"] = meta.HotThermalStorageOutputs.dict except: pass + try: r["outputs"]["HotSensibleTES"] = meta.HotSensibleTESOutputs.dict + except: pass try: r["outputs"]["ColdThermalStorage"] = meta.ColdThermalStorageOutputs.dict except: pass try: r["outputs"]["CHP"] = meta.CHPOutputs.dict From fe109f95e06093822e32fa238c5a5ad5e9a1487c Mon Sep 17 00:00:00 2001 From: Kadlec Date: Fri, 11 Jul 2025 09:00:11 -0600 Subject: [PATCH 12/57] updating add-cst-reopt reopt.jl version --- julia_src/Manifest.toml | 4 ++-- ...lter_hotsensibletesinputs_meta_and_more.py | 24 +++++++++++++++++++ reoptjl/models.py | 14 +++++------ reoptjl/validators.py | 4 ++-- reoptjl/views.py | 10 ++++---- 5 files changed, 40 insertions(+), 16 deletions(-) create mode 100644 reoptjl/migrations/0094_alter_hotsensibletesinputs_meta_and_more.py diff --git a/julia_src/Manifest.toml b/julia_src/Manifest.toml index a4a411c0b..3331f357e 100644 --- a/julia_src/Manifest.toml +++ b/julia_src/Manifest.toml @@ -932,11 +932,11 @@ uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" [[deps.REopt]] deps = ["ArchGDAL", "CSV", "CoolProp", "DataFrames", "Dates", "DelimitedFiles", "HTTP", "JLD", "JSON", "JuMP", "LinDistFlow", "LinearAlgebra", "Logging", "MathOptInterface", "Requires", "Roots", "Statistics", "TestEnv"] -git-tree-sha1 = "ff57946772fdc45adfef3ce0b6eb80a2926a2ae2" +git-tree-sha1 = "eacd32405778ae61363a2f25a89b50edec487756" repo-rev = "add-cst-reopt" repo-url = "https://github.com/NREL/REopt.jl.git" uuid = "d36ad4e8-d74a-4f7a-ace1-eaea049febf6" -version = "0.51.1" +version = "0.53.2" [[deps.Random]] deps = ["SHA"] diff --git a/reoptjl/migrations/0094_alter_hotsensibletesinputs_meta_and_more.py b/reoptjl/migrations/0094_alter_hotsensibletesinputs_meta_and_more.py new file mode 100644 index 000000000..f44992aff --- /dev/null +++ b/reoptjl/migrations/0094_alter_hotsensibletesinputs_meta_and_more.py @@ -0,0 +1,24 @@ +# Generated by Django 4.0.7 on 2025-05-09 17:44 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('reoptjl', '0093_hotsensibletesinputs_hotsensibletesoutputs_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='hotsensibletesinputs', + name='meta', + field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, related_name='HotSensibleTesInputs', serialize=False, to='reoptjl.apimeta'), + ), + migrations.AlterField( + model_name='hotsensibletesoutputs', + name='meta', + field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, related_name='HotSensibleTesOutputs', serialize=False, to='reoptjl.apimeta'), + ), + ] diff --git a/reoptjl/models.py b/reoptjl/models.py index e31b38771..ffa239661 100644 --- a/reoptjl/models.py +++ b/reoptjl/models.py @@ -6880,13 +6880,13 @@ def clean(self): # perform custom validation here. pass -class HotSensibleTESInputs(BaseModel, models.Model): - key = "HotSensibleTES" +class HotSensibleTesInputs(BaseModel, models.Model): + key = "HotSensibleTes" meta = models.OneToOneField( APIMeta, on_delete=models.CASCADE, - related_name="HotSensibleTESInputs", + related_name="HotSensibleTesInputs", primary_key=True ) @@ -7072,13 +7072,13 @@ class HotSensibleTESInputs(BaseModel, models.Model): help_text="Number of charge hours" ) -class HotSensibleTESOutputs(BaseModel, models.Model): - key = "HotSensibleTESOutputs" +class HotSensibleTesOutputs(BaseModel, models.Model): + key = "HotSensibleTesOutputs" meta = models.OneToOneField( APIMeta, on_delete=models.CASCADE, - related_name="HotSensibleTESOutputs", + related_name="HotSensibleTesOutputs", primary_key=True ) size_gal = models.FloatField(null=True, blank=True) @@ -8971,7 +8971,7 @@ def filter_none_and_empty_array(d:dict): try: d["HotThermalStorage"] = filter_none_and_empty_array(meta.HotThermalStorageInputs.dict) except: pass - try: d["HotSensibleTES"] = filter_none_and_empty_array(meta.HotSensibleTESInputs.dict) + try: d["HotSensibleTes"] = filter_none_and_empty_array(meta.HotSensibleTesInputs.dict) except: pass try: d["ColdThermalStorage"] = filter_none_and_empty_array(meta.ColdThermalStorageInputs.dict) diff --git a/reoptjl/validators.py b/reoptjl/validators.py index 22cb082af..4f6dd1777 100644 --- a/reoptjl/validators.py +++ b/reoptjl/validators.py @@ -5,7 +5,7 @@ FinancialInputs, BaseModel, Message, ElectricUtilityInputs, PVInputs, CSTInputs, ElectricStorageInputs, GeneratorInputs, WindInputs, SpaceHeatingLoadInputs, \ DomesticHotWaterLoadInputs, CHPInputs, CoolingLoadInputs, ExistingChillerInputs, HotThermalStorageInputs, ColdThermalStorageInputs, \ AbsorptionChillerInputs, BoilerInputs, SteamTurbineInputs, GHPInputs, ProcessHeatLoadInputs, ElectricHeaterInputs, ASHPSpaceHeaterInputs, \ - ASHPWaterHeaterInputs, CSTInputs, HotSensibleTESInputs + ASHPWaterHeaterInputs, CSTInputs, HotSensibleTesInputs from django.core.exceptions import ValidationError from pyproj import Proj from typing import Tuple @@ -77,7 +77,7 @@ def __init__(self, raw_inputs: dict, ghpghx_inputs_validation_errors=None): CHPInputs, BoilerInputs, HotThermalStorageInputs, - HotSensibleTESInputs, + HotSensibleTesInputs, ColdThermalStorageInputs, AbsorptionChillerInputs, SteamTurbineInputs, diff --git a/reoptjl/views.py b/reoptjl/views.py index 84c3c80de..fd2112904 100644 --- a/reoptjl/views.py +++ b/reoptjl/views.py @@ -18,7 +18,7 @@ FinancialInputs, FinancialOutputs, UserUnlinkedRuns, BoilerInputs, BoilerOutputs, SteamTurbineInputs, \ SteamTurbineOutputs, GHPInputs, GHPOutputs, ProcessHeatLoadInputs, ElectricHeaterInputs, ElectricHeaterOutputs, \ ASHPSpaceHeaterInputs, ASHPSpaceHeaterOutputs, ASHPWaterHeaterInputs, ASHPWaterHeaterOutputs, PortfolioUnlinkedRuns, \ - CSTInputs, CSTOutputs, HotSensibleTESInputs, HotSensibleTESOutputs + CSTInputs, CSTOutputs, HotSensibleTesInputs, HotSensibleTesOutputs import os import requests @@ -71,7 +71,7 @@ def help(request): d["ExistingBoiler"] = ExistingBoilerInputs.info_dict(ExistingBoilerInputs) d["Boiler"] = BoilerInputs.info_dict(BoilerInputs) d["HotThermalStorage"] = HotThermalStorageInputs.info_dict(HotThermalStorageInputs) - d["HotSensibleTES"] = HotSensibleTESInputs.info_dict(HotSensibleTESInputs) + d["HotSensibleTes"] = HotSensibleTesInputs.info_dict(HotSensibleTesInputs) d["ColdThermalStorage"] = ColdThermalStorageInputs.info_dict(ColdThermalStorageInputs) d["SpaceHeatingLoad"] = SpaceHeatingLoadInputs.info_dict(SpaceHeatingLoadInputs) d["DomesticHotWaterLoad"] = DomesticHotWaterLoadInputs.info_dict(DomesticHotWaterLoadInputs) @@ -124,7 +124,7 @@ def outputs(request): d["ExistingBoiler"] = ExistingBoilerOutputs.info_dict(ExistingBoilerOutputs) d["Boiler"] = BoilerOutputs.info_dict(BoilerOutputs) d["HotThermalStorage"] = HotThermalStorageOutputs.info_dict(HotThermalStorageOutputs) - d["HotSensibleTES"] = HotSensibleTESOutputs.info_dict(HotSensibleTESOutputs) + d["HotSensibleTes"] = HotSensibleTesOutputs.info_dict(HotSensibleTesOutputs) d["ColdThermalStorage"] = ColdThermalStorageOutputs.info_dict(ColdThermalStorageOutputs) d["Site"] = SiteOutputs.info_dict(SiteOutputs) d["HeatingLoad"] = HeatingLoadOutputs.info_dict(HeatingLoadOutputs) @@ -238,7 +238,7 @@ def results(request, run_uuid): try: r["inputs"]["HotThermalStorage"] = meta.HotThermalStorageInputs.dict except: pass - try: r["inputs"]["HotSensibleTES"] = meta.HotSensibleTESInputs.dict + try: r["inputs"]["HotSensibleTes"] = meta.HotSensibleTesInputs.dict except: pass try: r["inputs"]["ColdThermalStorage"] = meta.ColdThermalStorageInputs.dict @@ -335,7 +335,7 @@ def results(request, run_uuid): try: r["outputs"]["HotThermalStorage"] = meta.HotThermalStorageOutputs.dict except: pass - try: r["outputs"]["HotSensibleTES"] = meta.HotSensibleTESOutputs.dict + try: r["outputs"]["HotSensibleTes"] = meta.HotSensibleTesOutputs.dict except: pass try: r["outputs"]["ColdThermalStorage"] = meta.ColdThermalStorageOutputs.dict except: pass From a5a9252e6989b8d6e684142fb8550327000e2a4f Mon Sep 17 00:00:00 2001 From: bill-becker Date: Fri, 8 Aug 2025 12:11:14 -0600 Subject: [PATCH 13/57] Merge migrations after merging develop (through ponderosa) --- reoptjl/migrations/0095_merge_20250808_1803.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 reoptjl/migrations/0095_merge_20250808_1803.py diff --git a/reoptjl/migrations/0095_merge_20250808_1803.py b/reoptjl/migrations/0095_merge_20250808_1803.py new file mode 100644 index 000000000..f89400f09 --- /dev/null +++ b/reoptjl/migrations/0095_merge_20250808_1803.py @@ -0,0 +1,14 @@ +# Generated by Django 4.0.7 on 2025-08-08 18:03 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('reoptjl', '0092_merge_20250613_0525'), + ('reoptjl', '0094_alter_hotsensibletesinputs_meta_and_more'), + ] + + operations = [ + ] From 5a555b7736584b8ee3a26c5e069cf5d7c96d148b Mon Sep 17 00:00:00 2001 From: Kadlec Date: Mon, 11 Aug 2025 11:14:01 -0600 Subject: [PATCH 14/57] updating REopt.jl pointer to updated version of REopt.jl add-cst-reopt branch --- julia_src/Manifest.toml | 2 +- .../migrations/0095_merge_20250729_1616.py | 14 +++++++++++ ...lter_cstinputs_inlet_temp_degf_and_more.py | 24 +++++++++++++++++++ reoptjl/models.py | 8 +++---- 4 files changed, 43 insertions(+), 5 deletions(-) create mode 100644 reoptjl/migrations/0095_merge_20250729_1616.py create mode 100644 reoptjl/migrations/0096_alter_cstinputs_inlet_temp_degf_and_more.py diff --git a/julia_src/Manifest.toml b/julia_src/Manifest.toml index 3331f357e..e3b8a9be2 100644 --- a/julia_src/Manifest.toml +++ b/julia_src/Manifest.toml @@ -932,7 +932,7 @@ uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" [[deps.REopt]] deps = ["ArchGDAL", "CSV", "CoolProp", "DataFrames", "Dates", "DelimitedFiles", "HTTP", "JLD", "JSON", "JuMP", "LinDistFlow", "LinearAlgebra", "Logging", "MathOptInterface", "Requires", "Roots", "Statistics", "TestEnv"] -git-tree-sha1 = "eacd32405778ae61363a2f25a89b50edec487756" +git-tree-sha1 = "eebbbc1a902bc06edf86dfc784bb264d46ca0ae8" repo-rev = "add-cst-reopt" repo-url = "https://github.com/NREL/REopt.jl.git" uuid = "d36ad4e8-d74a-4f7a-ace1-eaea049febf6" diff --git a/reoptjl/migrations/0095_merge_20250729_1616.py b/reoptjl/migrations/0095_merge_20250729_1616.py new file mode 100644 index 000000000..fe7b9dcc7 --- /dev/null +++ b/reoptjl/migrations/0095_merge_20250729_1616.py @@ -0,0 +1,14 @@ +# Generated by Django 4.0.7 on 2025-07-29 16:16 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('reoptjl', '0092_merge_20250613_0525'), + ('reoptjl', '0094_alter_hotsensibletesinputs_meta_and_more'), + ] + + operations = [ + ] diff --git a/reoptjl/migrations/0096_alter_cstinputs_inlet_temp_degf_and_more.py b/reoptjl/migrations/0096_alter_cstinputs_inlet_temp_degf_and_more.py new file mode 100644 index 000000000..0597152a5 --- /dev/null +++ b/reoptjl/migrations/0096_alter_cstinputs_inlet_temp_degf_and_more.py @@ -0,0 +1,24 @@ +# Generated by Django 4.0.7 on 2025-08-08 19:25 + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('reoptjl', '0095_merge_20250729_1616'), + ] + + operations = [ + migrations.AlterField( + model_name='cstinputs', + name='inlet_temp_degF', + field=models.FloatField(blank=True, default=400, help_text="This is the temperature at which your process needs the heat transfer fluid specified above to be at when entering your facility. In other words, this is your 'hot' temperature.", validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(750)]), + ), + migrations.AlterField( + model_name='cstinputs', + name='outlet_temp_degF', + field=models.FloatField(blank=True, default=70, help_text="This is the temperature at which your the heat transfer fluid specified above returns from your process after heat has been extracted. In other words, this is your cold' temperature. If you have an open system, the inlet temperature will be assumed to be ambient temperature (20 C / 68 F).", validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(750)]), + ), + ] diff --git a/reoptjl/models.py b/reoptjl/models.py index 8756c3515..066b314f0 100644 --- a/reoptjl/models.py +++ b/reoptjl/models.py @@ -8845,18 +8845,18 @@ class CSTInputs(BaseModel, models.Model): help_text="Maximum CST size constraint for optimization (upper bound on additional capacity beyond existing_kw). Set to zero to disable PV" ) inlet_temp_degF = models.FloatField( - default=500, + default=400, validators=[ - MinValueValidator(300), + MinValueValidator(0), MaxValueValidator(750) ], blank=True, help_text="This is the temperature at which your process needs the heat transfer fluid specified above to be at when entering your facility. In other words, this is your 'hot' temperature." ) outlet_temp_degF = models.FloatField( - default=400, + default=70, validators=[ - MinValueValidator(300), + MinValueValidator(0), MaxValueValidator(750) ], blank=True, From 5581d52a079342eb2b0186d65269d460fabac9d9 Mon Sep 17 00:00:00 2001 From: Kadlec Date: Mon, 11 Aug 2025 12:19:04 -0600 Subject: [PATCH 15/57] merging migrations --- reoptjl/migrations/0097_merge_20250811_1817.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 reoptjl/migrations/0097_merge_20250811_1817.py diff --git a/reoptjl/migrations/0097_merge_20250811_1817.py b/reoptjl/migrations/0097_merge_20250811_1817.py new file mode 100644 index 000000000..bae465fc1 --- /dev/null +++ b/reoptjl/migrations/0097_merge_20250811_1817.py @@ -0,0 +1,14 @@ +# Generated by Django 4.0.7 on 2025-08-11 18:17 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('reoptjl', '0095_merge_20250808_1803'), + ('reoptjl', '0096_alter_cstinputs_inlet_temp_degf_and_more'), + ] + + operations = [ + ] From 40a723550f14d6386fe9d7c976521d243abb5d69 Mon Sep 17 00:00:00 2001 From: Kadlec Date: Fri, 15 Aug 2025 13:17:50 -0600 Subject: [PATCH 16/57] update version of REopt.jl for ProcessHeatLoad calculation --- julia_src/Manifest.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/julia_src/Manifest.toml b/julia_src/Manifest.toml index e3b8a9be2..0479bc003 100644 --- a/julia_src/Manifest.toml +++ b/julia_src/Manifest.toml @@ -932,7 +932,7 @@ uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" [[deps.REopt]] deps = ["ArchGDAL", "CSV", "CoolProp", "DataFrames", "Dates", "DelimitedFiles", "HTTP", "JLD", "JSON", "JuMP", "LinDistFlow", "LinearAlgebra", "Logging", "MathOptInterface", "Requires", "Roots", "Statistics", "TestEnv"] -git-tree-sha1 = "eebbbc1a902bc06edf86dfc784bb264d46ca0ae8" +git-tree-sha1 = "a9ab94db5676f6ed5b5bd770d2fc9e622f58c4df" repo-rev = "add-cst-reopt" repo-url = "https://github.com/NREL/REopt.jl.git" uuid = "d36ad4e8-d74a-4f7a-ace1-eaea049febf6" From dc44f746197fba0a31d1961f4f95631a3fcb673a Mon Sep 17 00:00:00 2001 From: bill-becker Date: Mon, 18 Aug 2025 12:06:40 -0600 Subject: [PATCH 17/57] Update REopt.jl#add-cst-reopt to latest git update --- julia_src/Manifest.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/julia_src/Manifest.toml b/julia_src/Manifest.toml index 0479bc003..e5b0a5aa5 100644 --- a/julia_src/Manifest.toml +++ b/julia_src/Manifest.toml @@ -932,7 +932,7 @@ uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" [[deps.REopt]] deps = ["ArchGDAL", "CSV", "CoolProp", "DataFrames", "Dates", "DelimitedFiles", "HTTP", "JLD", "JSON", "JuMP", "LinDistFlow", "LinearAlgebra", "Logging", "MathOptInterface", "Requires", "Roots", "Statistics", "TestEnv"] -git-tree-sha1 = "a9ab94db5676f6ed5b5bd770d2fc9e622f58c4df" +git-tree-sha1 = "29a2f7fb1ac75b4f712f6fee9fcce54c986a0541" repo-rev = "add-cst-reopt" repo-url = "https://github.com/NREL/REopt.jl.git" uuid = "d36ad4e8-d74a-4f7a-ace1-eaea049febf6" From e496c907cabd45dc7db74499df9b117e2c8a5976 Mon Sep 17 00:00:00 2001 From: bill-becker Date: Mon, 18 Aug 2025 12:07:17 -0600 Subject: [PATCH 18/57] Avoid error with inputs_to_update for CST which is not in that dictionary (TBD if it needs to be) --- reoptjl/src/process_results.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/reoptjl/src/process_results.py b/reoptjl/src/process_results.py index 96f75f192..db1f3c5ba 100644 --- a/reoptjl/src/process_results.py +++ b/reoptjl/src/process_results.py @@ -133,7 +133,6 @@ def update_inputs_in_database(inputs_to_update: dict, run_uuid: str) -> None: CHPInputs.objects.filter(meta__run_uuid=run_uuid).update(**inputs_to_update["CHP"]) if inputs_to_update["SteamTurbine"]: # Will be an empty dictionary if SteamTurbine is not considered SteamTurbineInputs.objects.filter(meta__run_uuid=run_uuid).update(**inputs_to_update["SteamTurbine"]) - if inputs_to_update["GHP"]: GHPInputs.objects.filter(meta__run_uuid=run_uuid).update(**inputs_to_update["GHP"]) if inputs_to_update["ExistingChiller"]: @@ -148,12 +147,13 @@ def update_inputs_in_database(inputs_to_update: dict, run_uuid: str) -> None: if inputs_to_update["ASHPWaterHeater"]: prune_update_fields(ASHPWaterHeaterInputs, inputs_to_update["ASHPWaterHeater"]) ASHPWaterHeaterInputs.objects.filter(meta__run_uuid=run_uuid).update(**inputs_to_update["ASHPWaterHeater"]) - if inputs_to_update["CST"]: - prune_update_fields(CSTInputs, inputs_to_update["CST"]) - CSTInputs.objects.filter(meta__run_uuid=run_uuid).update(**inputs_to_update["CST"]) if inputs_to_update["PV"]: prune_update_fields(PVInputs, inputs_to_update["PV"]) - PVInputs.objects.filter(meta__run_uuid=run_uuid).update(**inputs_to_update["PV"]) + PVInputs.objects.filter(meta__run_uuid=run_uuid).update(**inputs_to_update["PV"]) + # TODO CST is not added to this inputs_with_defaults_set_in_julia dictionary in http.jl, IF we need to update any CST inputs + if inputs_to_update.get("CST") is not None: + prune_update_fields(CSTInputs, inputs_to_update["CST"]) + CSTInputs.objects.filter(meta__run_uuid=run_uuid).update(**inputs_to_update["CST"]) except Exception as e: exc_type, exc_value, exc_traceback = sys.exc_info() debug_msg = "exc_type: {}; exc_value: {}; exc_traceback: {}".format( From b15bcd94555010d23f54a42455290e2da79f040d Mon Sep 17 00:00:00 2001 From: bill-becker Date: Mon, 18 Aug 2025 12:07:49 -0600 Subject: [PATCH 19/57] Add new output for SteamTurbine for hot_sensible_tes --- ..._hot_sensible_tes_series_mmbtu_per_hour.py | 19 +++++++++++++++++++ reoptjl/models.py | 5 +++++ 2 files changed, 24 insertions(+) create mode 100644 reoptjl/migrations/0098_steamturbineoutputs_thermal_to_hot_sensible_tes_series_mmbtu_per_hour.py diff --git a/reoptjl/migrations/0098_steamturbineoutputs_thermal_to_hot_sensible_tes_series_mmbtu_per_hour.py b/reoptjl/migrations/0098_steamturbineoutputs_thermal_to_hot_sensible_tes_series_mmbtu_per_hour.py new file mode 100644 index 000000000..f98e7fc37 --- /dev/null +++ b/reoptjl/migrations/0098_steamturbineoutputs_thermal_to_hot_sensible_tes_series_mmbtu_per_hour.py @@ -0,0 +1,19 @@ +# Generated by Django 4.0.7 on 2025-08-18 17:57 + +import django.contrib.postgres.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('reoptjl', '0097_merge_20250811_1817'), + ] + + operations = [ + migrations.AddField( + model_name='steamturbineoutputs', + name='thermal_to_hot_sensible_tes_series_mmbtu_per_hour', + field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), blank=True, default=list, size=None), + ), + ] diff --git a/reoptjl/models.py b/reoptjl/models.py index 066b314f0..7088ebe29 100644 --- a/reoptjl/models.py +++ b/reoptjl/models.py @@ -6797,6 +6797,11 @@ class SteamTurbineOutputs(BaseModel, models.Model): default = list ) + thermal_to_hot_sensible_tes_series_mmbtu_per_hour = ArrayField( + models.FloatField(null=True, blank=True), + blank=True, default=list + ) + class HotThermalStorageInputs(BaseModel, models.Model): key = "HotThermalStorage" From a747f61978c891477587057de8a2d27cfe081a46 Mon Sep 17 00:00:00 2001 From: adfarth Date: Tue, 19 Aug 2025 16:36:53 -0600 Subject: [PATCH 20/57] all updates --- CHANGELOG.md | 21 +++++++++++++++++++++ reoptjl/models.py | 42 +++++++++++++++++++++--------------------- 2 files changed, 42 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4cee93ce4..8442a61dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,27 @@ Classify the change according to the following categories: ##### Removed ### Patches +## misc-defaults +### Minor Updates +#### Changed +Update the following inputs from the previous --> new values: +- `Financial.offtaker_discount_rate_fraction`: 0.0638 --> 0.0624 +- `Financial.owner_discount_rate_fraction`: 0.0638 --> 0.0624 +- `Financial.elec_cost_escalation_rate_fraction`: 0.017 --> 0.0166 +- `Financial.existing_boiler_fuel_cost_escalation_rate_fraction `: 0.015 --> 0.0348 +- `Financial.boiler_fuel_cost_escalation_rate_fraction `: 0.015 --> 0.0348 +- `Financial.chp_fuel_cost_escalation_rate_fraction `: 0.015 --> 0.0348 +- `Financial.generator_fuel_cost_escalation_rate_fraction `: 0.012 --> 0.0197 +- `Generator.fuel_cost_per_gallon`: 3.61 --> 2.25 +- `CHP.federal_itc_fraction`: 0.3 --> 0.0 +- For all technologies eligible for first year bonus depreciation: `macrs_bonus_fraction`: 0.6 --> 0.4 (assuming 2025 implementation) +- `Wind.om_cost_per_kw`: 36.0 --> 42.0 +- `Wind.size_class_to_installed_cost` = Dict( + "residential"=> 6339.0, --> 7692.0 + "commercial"=> 4760.0, --> 5776.0 + "medium"=> 3137.0, --> 3807.0 + "large"=> 2386.0 --> 2896.0) + ## v3.13.0 ### Minor Updates #### Added diff --git a/reoptjl/models.py b/reoptjl/models.py index 2d48782ea..a8884d3f0 100644 --- a/reoptjl/models.py +++ b/reoptjl/models.py @@ -76,10 +76,10 @@ class MACRS_YEARS_CHOICES(models.IntegerChoices): } WIND_COST_DEFAULTS = { # size_class_to_installed_cost - "residential" : 6339.0, - "commercial" : 4760.0, - "medium" : 3137.0, - "large" : 2386.0 + "residential" : 7692.0, + "commercial" : 5776.0, + "medium" : 3807.0, + "large" : 2896.0 } def at_least_one_set(model, possible_sets): @@ -677,7 +677,7 @@ class FinancialInputs(BaseModel, models.Model): help_text="Analysis period in years. Must be integer." ) elec_cost_escalation_rate_fraction = models.FloatField( - default=0.017, + default=0.0166, validators=[ MinValueValidator(-1), MaxValueValidator(1) @@ -686,7 +686,7 @@ class FinancialInputs(BaseModel, models.Model): help_text="Annual nominal utility electricity cost escalation rate." ) offtaker_discount_rate_fraction = models.FloatField( - default=0.0638, + default=0.0624, validators=[ MinValueValidator(0), MaxValueValidator(1) @@ -714,7 +714,7 @@ class FinancialInputs(BaseModel, models.Model): help_text="Annual nominal O&M cost escalation rate" ) owner_discount_rate_fraction = models.FloatField( - default=0.0638, + default=0.0624, validators=[ MinValueValidator(0), MaxValueValidator(1) @@ -906,7 +906,7 @@ class FinancialInputs(BaseModel, models.Model): help_text=("Annual nominal escalation rate of the public health cost of 1 tonne of PM2.5 emissions (as a decimal). The default value is calculated from the EASIUR model for a height of 150m.") ) generator_fuel_cost_escalation_rate_fraction = models.FloatField( - default=0.012, + default=0.0197, validators=[ MinValueValidator(-1), MaxValueValidator(1) @@ -915,7 +915,7 @@ class FinancialInputs(BaseModel, models.Model): help_text=("Annual nominal boiler fuel cost escalation rate") ) existing_boiler_fuel_cost_escalation_rate_fraction = models.FloatField( - default=0.015, + default=0.0348, validators=[ MinValueValidator(-1), MaxValueValidator(1) @@ -924,7 +924,7 @@ class FinancialInputs(BaseModel, models.Model): help_text=("Annual nominal existing boiler fuel cost escalation rate") ) boiler_fuel_cost_escalation_rate_fraction = models.FloatField( - default=0.015, + default=0.0348, validators=[ MinValueValidator(-1), MaxValueValidator(1) @@ -933,7 +933,7 @@ class FinancialInputs(BaseModel, models.Model): help_text=("Annual nominal boiler fuel cost escalation rate") ) chp_fuel_cost_escalation_rate_fraction = models.FloatField( - default=0.015, + default=0.0348, validators=[ MinValueValidator(-1), MaxValueValidator(1) @@ -2785,7 +2785,7 @@ class PV_LOCATION_CHOICES(models.TextChoices): help_text="Duration over which accelerated depreciation will occur. Set to zero to disable" ) macrs_bonus_fraction = models.FloatField( - default=0.6, + default=0.4, validators=[ MinValueValidator(0), MaxValueValidator(1) @@ -3214,7 +3214,7 @@ class WIND_SIZE_CLASS_CHOICES(models.TextChoices): help_text="Installed cost in $/kW. Default cost is determined based on size_class." ) om_cost_per_kw = models.FloatField( - default=36, + default=42, validators=[ MinValueValidator(0), MaxValueValidator(1.0e3) @@ -3229,7 +3229,7 @@ class WIND_SIZE_CLASS_CHOICES(models.TextChoices): help_text="Duration over which accelerated depreciation will occur. Set to zero to disable" ) macrs_bonus_fraction = models.FloatField( - default=0.6, + default=0.4, validators=[ MinValueValidator(0), MaxValueValidator(1) @@ -3648,7 +3648,7 @@ class ElectricStorageInputs(BaseModel, models.Model): help_text="Duration over which accelerated depreciation will occur. Set to zero to disable" ) macrs_bonus_fraction = models.FloatField( - default=0.6, + default=0.4, validators=[ MinValueValidator(0), MaxValueValidator(1) @@ -3809,7 +3809,7 @@ class GeneratorInputs(BaseModel, models.Model): help_text="Diesel generator per unit production (variable) operations and maintenance costs in $/kWh" ) fuel_cost_per_gallon = models.FloatField( - default=3.0, + default=2.25, validators=[ MinValueValidator(0.0), MaxValueValidator(1.0e2) @@ -4438,7 +4438,7 @@ class CHPInputs(BaseModel, models.Model): help_text="Duration over which accelerated depreciation will occur. Set to zero to disable" ) macrs_bonus_fraction = models.FloatField( - default=0.6, + default=0.4, validators=[ MinValueValidator(0), MaxValueValidator(1) @@ -4456,7 +4456,7 @@ class CHPInputs(BaseModel, models.Model): help_text="Percent of the ITC value by which depreciable basis is reduced" ) federal_itc_fraction = models.FloatField( - default=0.3, + default=0.0, validators=[ MinValueValidator(0), MaxValueValidator(1) @@ -6900,7 +6900,7 @@ class HotThermalStorageInputs(BaseModel, models.Model): help_text="Duration over which accelerated depreciation will occur. Set to zero to disable" ) macrs_bonus_fraction = models.FloatField( - default=0.6, + default=0.4, validators=[ MinValueValidator(0), MaxValueValidator(1) @@ -7104,7 +7104,7 @@ class ColdThermalStorageInputs(BaseModel, models.Model): help_text="Duration over which accelerated depreciation will occur. Set to zero to disable" ) macrs_bonus_fraction = models.FloatField( - default=0.6, + default=0.4, validators=[ MinValueValidator(0), MaxValueValidator(1) @@ -8405,7 +8405,7 @@ class GHPInputs(BaseModel, models.Model): help_text="Duration over which accelerated depreciation will occur. Set to zero to disable" ) macrs_bonus_fraction = models.FloatField( - default=0.6, + default=0.4, validators=[ MinValueValidator(0), MaxValueValidator(1) From 3c052f7fb2cfe34c176e13fdf0ffe0970b091fc6 Mon Sep 17 00:00:00 2001 From: Kadlec Date: Wed, 20 Aug 2025 16:45:21 -0600 Subject: [PATCH 21/57] updating REopt.jl add-cst-reopt version and defaults for CST --- julia_src/Manifest.toml | 2 +- ..._alter_cstinputs_can_serve_dhw_and_more.py | 29 +++++++++++++++++++ reoptjl/models.py | 6 ++-- 3 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 reoptjl/migrations/0098_alter_cstinputs_can_serve_dhw_and_more.py diff --git a/julia_src/Manifest.toml b/julia_src/Manifest.toml index 0479bc003..f0b3c7bba 100644 --- a/julia_src/Manifest.toml +++ b/julia_src/Manifest.toml @@ -932,7 +932,7 @@ uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" [[deps.REopt]] deps = ["ArchGDAL", "CSV", "CoolProp", "DataFrames", "Dates", "DelimitedFiles", "HTTP", "JLD", "JSON", "JuMP", "LinDistFlow", "LinearAlgebra", "Logging", "MathOptInterface", "Requires", "Roots", "Statistics", "TestEnv"] -git-tree-sha1 = "a9ab94db5676f6ed5b5bd770d2fc9e622f58c4df" +git-tree-sha1 = "42984357840ab164effe5df72c747848ae40b9b2" repo-rev = "add-cst-reopt" repo-url = "https://github.com/NREL/REopt.jl.git" uuid = "d36ad4e8-d74a-4f7a-ace1-eaea049febf6" diff --git a/reoptjl/migrations/0098_alter_cstinputs_can_serve_dhw_and_more.py b/reoptjl/migrations/0098_alter_cstinputs_can_serve_dhw_and_more.py new file mode 100644 index 000000000..bd5fb5952 --- /dev/null +++ b/reoptjl/migrations/0098_alter_cstinputs_can_serve_dhw_and_more.py @@ -0,0 +1,29 @@ +# Generated by Django 4.0.7 on 2025-08-20 22:36 + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('reoptjl', '0097_merge_20250811_1817'), + ] + + operations = [ + migrations.AlterField( + model_name='cstinputs', + name='can_serve_dhw', + field=models.BooleanField(blank=True, default=False, help_text='Boolean indicator if CST can serve hot water load', null=True), + ), + migrations.AlterField( + model_name='cstinputs', + name='can_serve_space_heating', + field=models.BooleanField(blank=True, default=False, help_text='Boolean indicator if CST can serve space heating load', null=True), + ), + migrations.AlterField( + model_name='cstinputs', + name='macrs_bonus_fraction', + field=models.FloatField(blank=True, default=0.6, help_text='Percent of upfront project costs to depreciate in year one in addition to scheduled depreciation', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1)]), + ), + ] diff --git a/reoptjl/models.py b/reoptjl/models.py index 066b314f0..3e5cba552 100644 --- a/reoptjl/models.py +++ b/reoptjl/models.py @@ -8905,7 +8905,7 @@ class CSTInputs(BaseModel, models.Model): help_text="Duration over which accelerated depreciation will occur. Set to zero to disable" ) macrs_bonus_fraction = models.FloatField( - default=0.0, + default=0.6, validators=[ MinValueValidator(0), MaxValueValidator(1) @@ -8934,13 +8934,13 @@ class CSTInputs(BaseModel, models.Model): help_text="Boolean indicator if CST can supply steam to the steam turbine for electric production" ) can_serve_dhw = models.BooleanField( - default=True, + default=False, null=True, blank=True, help_text="Boolean indicator if CST can serve hot water load" ) can_serve_space_heating = models.BooleanField( - default=True, + default=False, null=True, blank=True, help_text="Boolean indicator if CST can serve space heating load" From ab9eea8b904ef0ce53a730c6535a4c8dc7906a50 Mon Sep 17 00:00:00 2001 From: adfarth Date: Thu, 21 Aug 2025 08:15:59 -0600 Subject: [PATCH 22/57] update tests --- reoptjl/test/posts/outage.json | 3 ++- reoptjl/test/test_validator.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/reoptjl/test/posts/outage.json b/reoptjl/test/posts/outage.json index 5658437c5..b7a501c47 100644 --- a/reoptjl/test/posts/outage.json +++ b/reoptjl/test/posts/outage.json @@ -34,7 +34,8 @@ "min_kw": 150.0, "max_kw": 150.0, "macrs_bonus_fraction": 0.8, - "macrs_option_years": 0 + "macrs_option_years": 0, + "federal_itc_fraction": 0.3 }, "PV": { "min_kw": 282.3629, diff --git a/reoptjl/test/test_validator.py b/reoptjl/test/test_validator.py index 8e0c1c6d3..2e9d5c24b 100644 --- a/reoptjl/test/test_validator.py +++ b/reoptjl/test/test_validator.py @@ -102,7 +102,7 @@ def test_off_grid_defaults_overrides(self): self.assertAlmostEqual(validator.models["Wind"].operating_reserve_required_fraction, 0.5) self.assertAlmostEqual(validator.models["PV"].operating_reserve_required_fraction, 0.25) - self.assertEqual(validator.models["Wind"].installed_cost_per_kw, 4760.0) # set based on size_class + self.assertEqual(validator.models["Wind"].installed_cost_per_kw, 5776.0) # set based on size_class (commercial) self.assertAlmostEqual(validator.models["ElectricLoad"].operating_reserve_required_fraction, 0.1) self.assertAlmostEqual(validator.models["ElectricLoad"].critical_load_fraction, 1.0) From 51a02e02ef04d2b31212a5f05f8b6653ebeff8fa Mon Sep 17 00:00:00 2001 From: adfarth Date: Thu, 21 Aug 2025 08:28:16 -0600 Subject: [PATCH 23/57] point to feature branch --- julia_src/Manifest.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/julia_src/Manifest.toml b/julia_src/Manifest.toml index 499e0c00a..fb7eca19a 100644 --- a/julia_src/Manifest.toml +++ b/julia_src/Manifest.toml @@ -922,7 +922,9 @@ uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" [[deps.REopt]] deps = ["ArchGDAL", "CSV", "CoolProp", "DataFrames", "Dates", "DelimitedFiles", "HTTP", "JLD", "JSON", "JuMP", "LinDistFlow", "LinearAlgebra", "Logging", "MathOptInterface", "Requires", "Roots", "Statistics", "TestEnv"] -git-tree-sha1 = "4db74039055f6084d8bd45304de9de856462d917" +git-tree-sha1 = "7152b01634998853fac6b0fd386d46d22b83c474" +repo-rev = "misc-defaults" +repo-url = "https://github.com/NREL/REopt.jl.git" uuid = "d36ad4e8-d74a-4f7a-ace1-eaea049febf6" version = "0.53.2" From a4cd4b8bfdadaa67f1419375df029e4a05e4aea8 Mon Sep 17 00:00:00 2001 From: adfarth Date: Thu, 21 Aug 2025 09:20:07 -0600 Subject: [PATCH 24/57] Update test_http_endpoints.py --- reoptjl/test/test_http_endpoints.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reoptjl/test/test_http_endpoints.py b/reoptjl/test/test_http_endpoints.py index 8104a6197..23303c196 100644 --- a/reoptjl/test/test_http_endpoints.py +++ b/reoptjl/test/test_http_endpoints.py @@ -35,7 +35,7 @@ def test_chp_defaults(self): self.assertEqual(http_response["prime_mover"], "combustion_turbine") self.assertEqual(http_response["size_class"], 2) self.assertGreater(http_response["chp_elec_size_heuristic_kw"], 3500.0) - self.assertEqual(http_response["default_inputs"]["federal_itc_fraction"], 0.3) + self.assertEqual(http_response["default_inputs"]["federal_itc_fraction"], 0.0) inputs = { "prime_mover": "micro_turbine", From 08dd253394064e7b3f0c418612d2e04d43ed5a4a Mon Sep 17 00:00:00 2001 From: Kadlec Date: Thu, 21 Aug 2025 09:50:39 -0600 Subject: [PATCH 25/57] merging migrations --- reoptjl/migrations/0099_merge_20250821_1548.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 reoptjl/migrations/0099_merge_20250821_1548.py diff --git a/reoptjl/migrations/0099_merge_20250821_1548.py b/reoptjl/migrations/0099_merge_20250821_1548.py new file mode 100644 index 000000000..c485f61e1 --- /dev/null +++ b/reoptjl/migrations/0099_merge_20250821_1548.py @@ -0,0 +1,14 @@ +# Generated by Django 4.0.7 on 2025-08-21 15:48 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('reoptjl', '0098_alter_cstinputs_can_serve_dhw_and_more'), + ('reoptjl', '0098_steamturbineoutputs_thermal_to_hot_sensible_tes_series_mmbtu_per_hour'), + ] + + operations = [ + ] From a33581334c162af165b53aef6737788a2362f6cb Mon Sep 17 00:00:00 2001 From: adfarth Date: Thu, 21 Aug 2025 10:13:53 -0600 Subject: [PATCH 26/57] Update test_http_endpoints.py --- reoptjl/test/test_http_endpoints.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reoptjl/test/test_http_endpoints.py b/reoptjl/test/test_http_endpoints.py index 23303c196..14771a2c3 100644 --- a/reoptjl/test/test_http_endpoints.py +++ b/reoptjl/test/test_http_endpoints.py @@ -50,7 +50,7 @@ def test_chp_defaults(self): http_response = response.json() # Check the endpoint logic with the expected selection - self.assertEqual(http_response["default_inputs"]["federal_itc_fraction"], 0.3) + self.assertEqual(http_response["default_inputs"]["federal_itc_fraction"], 0.0) inputs = { "prime_mover": "combustion_turbine", From 09117077a24617fde72c545cfddaa7f495b9e2bc Mon Sep 17 00:00:00 2001 From: Kadlec Date: Fri, 22 Aug 2025 13:45:41 -0600 Subject: [PATCH 27/57] updating the REopt.jl version of add-cst-reopt for the API --- julia_src/Manifest.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/julia_src/Manifest.toml b/julia_src/Manifest.toml index f0b3c7bba..5bbf1ceda 100644 --- a/julia_src/Manifest.toml +++ b/julia_src/Manifest.toml @@ -932,7 +932,7 @@ uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" [[deps.REopt]] deps = ["ArchGDAL", "CSV", "CoolProp", "DataFrames", "Dates", "DelimitedFiles", "HTTP", "JLD", "JSON", "JuMP", "LinDistFlow", "LinearAlgebra", "Logging", "MathOptInterface", "Requires", "Roots", "Statistics", "TestEnv"] -git-tree-sha1 = "42984357840ab164effe5df72c747848ae40b9b2" +git-tree-sha1 = "d1fb1a0f5b6fedb581fdaf86b95ca816011def3d" repo-rev = "add-cst-reopt" repo-url = "https://github.com/NREL/REopt.jl.git" uuid = "d36ad4e8-d74a-4f7a-ace1-eaea049febf6" From 8fb8c342b0f38cfa59b4697a1557c9097a34d818 Mon Sep 17 00:00:00 2001 From: adfarth Date: Fri, 22 Aug 2025 14:56:00 -0600 Subject: [PATCH 28/57] update macrs --- CHANGELOG.md | 3 ++- reoptjl/models.py | 16 ++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8442a61dc..bb8f07c56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,8 +38,9 @@ Update the following inputs from the previous --> new values: - `Financial.chp_fuel_cost_escalation_rate_fraction `: 0.015 --> 0.0348 - `Financial.generator_fuel_cost_escalation_rate_fraction `: 0.012 --> 0.0197 - `Generator.fuel_cost_per_gallon`: 3.61 --> 2.25 +- `ColdThermalStorage`, `HotThermalStorage`, `ElectricStorage` `macrs_option_years` 7 --> 5 +- `ColdThermalStorage`, `HotThermalStorage`, `ElectricStorage`, `PV`, `Wind` `macrs_bonus_fraction` 0.6 --> 1.0 - `CHP.federal_itc_fraction`: 0.3 --> 0.0 -- For all technologies eligible for first year bonus depreciation: `macrs_bonus_fraction`: 0.6 --> 0.4 (assuming 2025 implementation) - `Wind.om_cost_per_kw`: 36.0 --> 42.0 - `Wind.size_class_to_installed_cost` = Dict( "residential"=> 6339.0, --> 7692.0 diff --git a/reoptjl/models.py b/reoptjl/models.py index a8884d3f0..ac991675b 100644 --- a/reoptjl/models.py +++ b/reoptjl/models.py @@ -2785,7 +2785,7 @@ class PV_LOCATION_CHOICES(models.TextChoices): help_text="Duration over which accelerated depreciation will occur. Set to zero to disable" ) macrs_bonus_fraction = models.FloatField( - default=0.4, + default=1.0, validators=[ MinValueValidator(0), MaxValueValidator(1) @@ -3229,7 +3229,7 @@ class WIND_SIZE_CLASS_CHOICES(models.TextChoices): help_text="Duration over which accelerated depreciation will occur. Set to zero to disable" ) macrs_bonus_fraction = models.FloatField( - default=0.4, + default=1.0, validators=[ MinValueValidator(0), MaxValueValidator(1) @@ -3642,13 +3642,13 @@ class ElectricStorageInputs(BaseModel, models.Model): help_text="Annual O&M cost as a fraction of installed cost." ) macrs_option_years = models.IntegerField( - default=MACRS_YEARS_CHOICES.SEVEN, + default=MACRS_YEARS_CHOICES.FIVE, choices=MACRS_YEARS_CHOICES.choices, blank=True, help_text="Duration over which accelerated depreciation will occur. Set to zero to disable" ) macrs_bonus_fraction = models.FloatField( - default=0.4, + default=1.0, validators=[ MinValueValidator(0), MaxValueValidator(1) @@ -6894,13 +6894,13 @@ class HotThermalStorageInputs(BaseModel, models.Model): help_text="Thermal energy-based cost of TES (e.g. volume of the tank)" ) macrs_option_years = models.IntegerField( - default=MACRS_YEARS_CHOICES.SEVEN, + default=MACRS_YEARS_CHOICES.FIVE, choices=MACRS_YEARS_CHOICES.choices, blank=True, help_text="Duration over which accelerated depreciation will occur. Set to zero to disable" ) macrs_bonus_fraction = models.FloatField( - default=0.4, + default=1.0, validators=[ MinValueValidator(0), MaxValueValidator(1) @@ -7098,13 +7098,13 @@ class ColdThermalStorageInputs(BaseModel, models.Model): help_text="Thermal energy-based cost of TES (e.g. volume of the tank)" ) macrs_option_years = models.IntegerField( - default=MACRS_YEARS_CHOICES.SEVEN, + default=MACRS_YEARS_CHOICES.FIVE, choices=MACRS_YEARS_CHOICES.choices, blank=True, help_text="Duration over which accelerated depreciation will occur. Set to zero to disable" ) macrs_bonus_fraction = models.FloatField( - default=0.4, + default=1.0, validators=[ MinValueValidator(0), MaxValueValidator(1) From 706f3c556fbdae816d873546d0f9b4a2dfe83da1 Mon Sep 17 00:00:00 2001 From: Kadlec Date: Mon, 25 Aug 2025 12:17:11 -0600 Subject: [PATCH 29/57] adding HotSensibleTes functionaltiy to process_results.py --- reoptjl/src/process_results.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/reoptjl/src/process_results.py b/reoptjl/src/process_results.py index db1f3c5ba..cf7efceb5 100644 --- a/reoptjl/src/process_results.py +++ b/reoptjl/src/process_results.py @@ -8,7 +8,8 @@ REoptjlMessageOutputs, AbsorptionChillerOutputs, BoilerOutputs, SteamTurbineInputs, \ SteamTurbineOutputs, GHPInputs, GHPOutputs, ExistingChillerInputs, \ ElectricHeaterOutputs, ASHPSpaceHeaterOutputs, ASHPWaterHeaterOutputs, \ - SiteInputs, ASHPSpaceHeaterInputs, ASHPWaterHeaterInputs, CSTInputs, CSTOutputs, PVInputs + SiteInputs, ASHPSpaceHeaterInputs, ASHPWaterHeaterInputs, CSTInputs, CSTOutputs, PVInputs, \ + HotSensibleTesInputs, HotSensibleTesOutputs import numpy as np import sys import traceback as tb @@ -91,7 +92,9 @@ def process_results(results: dict, run_uuid: str) -> None: if "ASHPWaterHeater" in results.keys(): ASHPWaterHeaterOutputs.create(meta=meta, **results["ASHPWaterHeater"]).save() if "CST" in results.keys(): - CSTOutputs.create(meta=meta, **results["CST"]).save() + CSTOutputs.create(meta=meta, **results["CST"]).save() + if "HotSensibleTes" in results.keys(): + HotSensibleTesOutputs.create(meta=meta, **results["HotSensibleTes"]).save() # TODO process rest of results except Exception as e: exc_type, exc_value, exc_traceback = sys.exc_info() @@ -154,6 +157,9 @@ def update_inputs_in_database(inputs_to_update: dict, run_uuid: str) -> None: if inputs_to_update.get("CST") is not None: prune_update_fields(CSTInputs, inputs_to_update["CST"]) CSTInputs.objects.filter(meta__run_uuid=run_uuid).update(**inputs_to_update["CST"]) + if inputs_to_update.get("HotSensibleTes") is not None: + prune_update_fields(HotSensibleTesInputs, inputs_to_update["HotSensibleTes"]) + HotSensibleTesInputs.objects.filter(meta__run_uuid=run_uuid).update(**inputs_to_update["HotSensibleTes"]) except Exception as e: exc_type, exc_value, exc_traceback = sys.exc_info() debug_msg = "exc_type: {}; exc_value: {}; exc_traceback: {}".format( From 3ef85d5abdba262df676284b99d83e7e64351042 Mon Sep 17 00:00:00 2001 From: Kadlec Date: Fri, 29 Aug 2025 12:19:24 -0600 Subject: [PATCH 30/57] renaming hot sensible tes to high temp thermal storage --- julia_src/Manifest.toml | 2 +- ...0_hightempthermalstorageinputs_and_more.py | 80 +++++++++++++++++++ reoptjl/models.py | 24 +++--- reoptjl/src/process_results.py | 12 +-- reoptjl/validators.py | 4 +- reoptjl/views.py | 12 +-- 6 files changed, 105 insertions(+), 29 deletions(-) create mode 100644 reoptjl/migrations/0100_hightempthermalstorageinputs_and_more.py diff --git a/julia_src/Manifest.toml b/julia_src/Manifest.toml index 5bbf1ceda..c37a6dff7 100644 --- a/julia_src/Manifest.toml +++ b/julia_src/Manifest.toml @@ -932,7 +932,7 @@ uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" [[deps.REopt]] deps = ["ArchGDAL", "CSV", "CoolProp", "DataFrames", "Dates", "DelimitedFiles", "HTTP", "JLD", "JSON", "JuMP", "LinDistFlow", "LinearAlgebra", "Logging", "MathOptInterface", "Requires", "Roots", "Statistics", "TestEnv"] -git-tree-sha1 = "d1fb1a0f5b6fedb581fdaf86b95ca816011def3d" +git-tree-sha1 = "a839010c66391e5aab327bd39a2d6f0bd255a275" repo-rev = "add-cst-reopt" repo-url = "https://github.com/NREL/REopt.jl.git" uuid = "d36ad4e8-d74a-4f7a-ace1-eaea049febf6" diff --git a/reoptjl/migrations/0100_hightempthermalstorageinputs_and_more.py b/reoptjl/migrations/0100_hightempthermalstorageinputs_and_more.py new file mode 100644 index 000000000..a5ded9368 --- /dev/null +++ b/reoptjl/migrations/0100_hightempthermalstorageinputs_and_more.py @@ -0,0 +1,80 @@ +# Generated by Django 4.0.7 on 2025-08-29 18:16 + +import django.contrib.postgres.fields +import django.core.validators +from django.db import migrations, models +import django.db.models.deletion +import reoptjl.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('reoptjl', '0099_merge_20250821_1548'), + ] + + operations = [ + migrations.CreateModel( + name='HighTempThermalStorageInputs', + fields=[ + ('meta', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, related_name='HighTempThermalStorageInputs', serialize=False, to='reoptjl.apimeta')), + ('min_kwh', models.FloatField(blank=True, default=0.0, help_text='Minimum TES volume (energy) size constraint for optimization', null=True, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1000000000.0)])), + ('max_kwh', models.FloatField(blank=True, default=0.0, help_text='Maximum TES volume (energy) size constraint for optimization. Set to zero to disable storage', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1000000000.0)])), + ('hot_temp_degF', models.FloatField(blank=True, default=1065.0, help_text='Hot-side supply water temperature from HotTES (top of tank) to the heating load', validators=[django.core.validators.MinValueValidator(200.0), django.core.validators.MaxValueValidator(2000.0)])), + ('cool_temp_degF', models.FloatField(blank=True, default=554.0, help_text='Cold-side return water temperature from the heating load to the HotTES (bottom of tank)', validators=[django.core.validators.MinValueValidator(200.0), django.core.validators.MaxValueValidator(2000.0)])), + ('internal_efficiency_fraction', models.FloatField(blank=True, default=0.999999, help_text='Thermal losses due to mixing from thermal power entering or leaving tank', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1.0)])), + ('soc_min_fraction', models.FloatField(blank=True, default=0.1, help_text='Minimum allowable battery state of charge as fraction of energy capacity.', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1.0)])), + ('soc_init_fraction', models.FloatField(blank=True, default=0.5, help_text='Battery state of charge at first hour of optimization as fraction of energy capacity.', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1.0)])), + ('installed_cost_per_kwh', models.FloatField(blank=True, default=1.5, help_text='Installed hot TES cost in $/kwh', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(10000.0)])), + ('om_cost_per_kwh', models.FloatField(blank=True, default=0.0, help_text='Annual hot TES operations and maintenance costs in $/kwh', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1000.0)])), + ('thermal_decay_rate_fraction', models.FloatField(blank=True, default=0.0004, help_text='Thermal energy-based cost of TES (e.g. volume of the tank)', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1.0)])), + ('macrs_option_years', models.IntegerField(blank=True, choices=[(0, 'Zero'), (5, 'Five'), (7, 'Seven')], default=7, help_text='Duration over which accelerated depreciation will occur. Set to zero to disable')), + ('macrs_bonus_fraction', models.FloatField(blank=True, default=0.6, help_text='Percent of upfront project costs to depreciate in year one in addition to scheduled depreciation', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1)])), + ('macrs_itc_reduction', models.FloatField(blank=True, default=0.0, help_text='Percent of the ITC value by which depreciable basis is reduced', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1)])), + ('total_itc_fraction', models.FloatField(blank=True, default=0.3, help_text='Total investment tax credit in percent applied toward capital costs', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1)])), + ('total_rebate_per_kwh', models.FloatField(blank=True, default=0.0, help_text='Rebate per unit installed energy capacity', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1000000000.0)])), + ('can_serve_dhw', models.BooleanField(blank=True, default=False, help_text='Boolean indicator if hot thermal storage can serve space heating load', null=True)), + ('can_serve_space_heating', models.BooleanField(blank=True, default=False, help_text='Boolean indicator if hot thermal storage can serve space heating load', null=True)), + ('can_serve_process_heat', models.BooleanField(blank=True, default=True, help_text='Boolean indicator if hot thermal storage can serve process heat load', null=True)), + ('supply_turbine_only', models.BooleanField(blank=True, default=False, help_text='Boolean indicator if hot thermal storage can serve only steam turbine', null=True)), + ('one_direction_flow', models.BooleanField(blank=True, default=False, help_text='Boolean indicator if hot thermal storage can only', null=True)), + ('num_charge_hours', models.FloatField(blank=True, default=4.0, help_text='Number of charge hours', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(10000.0)])), + ('num_discharge_hours', models.FloatField(blank=True, default=10.0, help_text='Number of charge hours', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(10000.0)])), + ], + bases=(reoptjl.models.BaseModel, models.Model), + ), + migrations.CreateModel( + name='HighTempThermalStorageOutputs', + fields=[ + ('meta', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, related_name='HighTempThermalStorageOutputs', serialize=False, to='reoptjl.apimeta')), + ('size_kwh', models.FloatField(blank=True, null=True)), + ('soc_series_fraction', django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), default=list, size=None)), + ('storage_to_load_series_mmbtu_per_hour', django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), default=list, size=None)), + ('storage_to_turbine_series_mmbtu_per_hour', django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), default=list, size=None)), + ], + bases=(reoptjl.models.BaseModel, models.Model), + ), + migrations.RemoveField( + model_name='hotsensibletesoutputs', + name='meta', + ), + migrations.RemoveField( + model_name='cstinputs', + name='capacity_factor_series', + ), + migrations.RemoveField( + model_name='cstoutputs', + name='thermal_to_hot_sensible_tes_series_mmbtu_per_hour', + ), + migrations.AddField( + model_name='cstinputs', + name='production_factor', + field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True), blank=True, default=list, help_text='Optional user-defined production factors for CST.', size=None), + ), + migrations.DeleteModel( + name='HotSensibleTESInputs', + ), + migrations.DeleteModel( + name='HotSensibleTESOutputs', + ), + ] diff --git a/reoptjl/models.py b/reoptjl/models.py index b7627c42c..1fb39ede3 100644 --- a/reoptjl/models.py +++ b/reoptjl/models.py @@ -7011,13 +7011,13 @@ def clean(self): # perform custom validation here. pass -class HotSensibleTesInputs(BaseModel, models.Model): - key = "HotSensibleTes" +class HighTempThermalStorageInputs(BaseModel, models.Model): + key = "HighTempThermalStorage" meta = models.OneToOneField( APIMeta, on_delete=models.CASCADE, - related_name="HotSensibleTesInputs", + related_name="HighTempThermalStorageInputs", primary_key=True ) @@ -7203,16 +7203,15 @@ class HotSensibleTesInputs(BaseModel, models.Model): help_text="Number of charge hours" ) -class HotSensibleTesOutputs(BaseModel, models.Model): - key = "HotSensibleTesOutputs" +class HighTempThermalStorageOutputs(BaseModel, models.Model): + key = "HighTempThermalStorageOutputs" meta = models.OneToOneField( APIMeta, on_delete=models.CASCADE, - related_name="HotSensibleTesOutputs", + related_name="HighTempThermalStorageOutputs", primary_key=True ) - size_gal = models.FloatField(null=True, blank=True) size_kwh = models.FloatField(null=True, blank=True) soc_series_fraction = ArrayField( models.FloatField(null=True, blank=True), @@ -8918,12 +8917,12 @@ class CSTInputs(BaseModel, models.Model): blank=True, help_text="Percent of upfront project costs to depreciate in year one in addition to scheduled depreciation" ) - capacity_factor_series = ArrayField( + production_factor = ArrayField( models.FloatField( blank=True ), default=list, blank=True, - help_text=("Optional user-defined capacity factors for CST.") + help_text=("Optional user-defined production factors for CST.") ) elec_consumption_factor_series = ArrayField( models.FloatField( @@ -9022,14 +9021,11 @@ class CSTOutputs(BaseModel, models.Model): models.FloatField(null=True, blank=True), blank=True, default=list ) + # Should this be changed? thermal_to_hot_sensible_tes_storage_series_mmbtu_per_hour = ArrayField( models.FloatField(null=True, blank=True), blank=True, default=list ) - thermal_to_hot_sensible_tes_series_mmbtu_per_hour = ArrayField( - models.FloatField(null=True, blank=True), - blank=True, default=list - ) thermal_to_steamturbine_series_mmbtu_per_hour = ArrayField( models.FloatField(null=True, blank=True), blank=True, default=list @@ -9130,7 +9126,7 @@ def filter_none_and_empty_array(d:dict): try: d["HotThermalStorage"] = filter_none_and_empty_array(meta.HotThermalStorageInputs.dict) except: pass - try: d["HotSensibleTes"] = filter_none_and_empty_array(meta.HotSensibleTesInputs.dict) + try: d["HighTempThermalStorage"] = filter_none_and_empty_array(meta.HighTempThermalStorageInputs.dict) except: pass try: d["ColdThermalStorage"] = filter_none_and_empty_array(meta.ColdThermalStorageInputs.dict) diff --git a/reoptjl/src/process_results.py b/reoptjl/src/process_results.py index cf7efceb5..c1faad9e7 100644 --- a/reoptjl/src/process_results.py +++ b/reoptjl/src/process_results.py @@ -9,7 +9,7 @@ SteamTurbineOutputs, GHPInputs, GHPOutputs, ExistingChillerInputs, \ ElectricHeaterOutputs, ASHPSpaceHeaterOutputs, ASHPWaterHeaterOutputs, \ SiteInputs, ASHPSpaceHeaterInputs, ASHPWaterHeaterInputs, CSTInputs, CSTOutputs, PVInputs, \ - HotSensibleTesInputs, HotSensibleTesOutputs + HighTempThermalStorageInputs, HighTempThermalStorageOutputs import numpy as np import sys import traceback as tb @@ -93,8 +93,8 @@ def process_results(results: dict, run_uuid: str) -> None: ASHPWaterHeaterOutputs.create(meta=meta, **results["ASHPWaterHeater"]).save() if "CST" in results.keys(): CSTOutputs.create(meta=meta, **results["CST"]).save() - if "HotSensibleTes" in results.keys(): - HotSensibleTesOutputs.create(meta=meta, **results["HotSensibleTes"]).save() + if "HighTempThermalStorage" in results.keys(): + HighTempThermalStorageOutputs.create(meta=meta, **results["HighTempThermalStorage"]).save() # TODO process rest of results except Exception as e: exc_type, exc_value, exc_traceback = sys.exc_info() @@ -157,9 +157,9 @@ def update_inputs_in_database(inputs_to_update: dict, run_uuid: str) -> None: if inputs_to_update.get("CST") is not None: prune_update_fields(CSTInputs, inputs_to_update["CST"]) CSTInputs.objects.filter(meta__run_uuid=run_uuid).update(**inputs_to_update["CST"]) - if inputs_to_update.get("HotSensibleTes") is not None: - prune_update_fields(HotSensibleTesInputs, inputs_to_update["HotSensibleTes"]) - HotSensibleTesInputs.objects.filter(meta__run_uuid=run_uuid).update(**inputs_to_update["HotSensibleTes"]) + if inputs_to_update.get("HighTempThermalStorage") is not None: + prune_update_fields(HighTempThermalStorageInputs, inputs_to_update["HighTempThermalStorage"]) + HighTempThermalStorageInputs.objects.filter(meta__run_uuid=run_uuid).update(**inputs_to_update["HighTempThermalStorage"]) except Exception as e: exc_type, exc_value, exc_traceback = sys.exc_info() debug_msg = "exc_type: {}; exc_value: {}; exc_traceback: {}".format( diff --git a/reoptjl/validators.py b/reoptjl/validators.py index 4f6dd1777..3429a0291 100644 --- a/reoptjl/validators.py +++ b/reoptjl/validators.py @@ -5,7 +5,7 @@ FinancialInputs, BaseModel, Message, ElectricUtilityInputs, PVInputs, CSTInputs, ElectricStorageInputs, GeneratorInputs, WindInputs, SpaceHeatingLoadInputs, \ DomesticHotWaterLoadInputs, CHPInputs, CoolingLoadInputs, ExistingChillerInputs, HotThermalStorageInputs, ColdThermalStorageInputs, \ AbsorptionChillerInputs, BoilerInputs, SteamTurbineInputs, GHPInputs, ProcessHeatLoadInputs, ElectricHeaterInputs, ASHPSpaceHeaterInputs, \ - ASHPWaterHeaterInputs, CSTInputs, HotSensibleTesInputs + ASHPWaterHeaterInputs, CSTInputs, HighTempThermalStorageInputs from django.core.exceptions import ValidationError from pyproj import Proj from typing import Tuple @@ -77,7 +77,7 @@ def __init__(self, raw_inputs: dict, ghpghx_inputs_validation_errors=None): CHPInputs, BoilerInputs, HotThermalStorageInputs, - HotSensibleTesInputs, + HighTempThermalStorageInputs, ColdThermalStorageInputs, AbsorptionChillerInputs, SteamTurbineInputs, diff --git a/reoptjl/views.py b/reoptjl/views.py index 1e9ab9615..8d7cd4ba4 100644 --- a/reoptjl/views.py +++ b/reoptjl/views.py @@ -18,7 +18,7 @@ FinancialInputs, FinancialOutputs, UserUnlinkedRuns, BoilerInputs, BoilerOutputs, SteamTurbineInputs, \ SteamTurbineOutputs, GHPInputs, GHPOutputs, ProcessHeatLoadInputs, ElectricHeaterInputs, ElectricHeaterOutputs, \ ASHPSpaceHeaterInputs, ASHPSpaceHeaterOutputs, ASHPWaterHeaterInputs, ASHPWaterHeaterOutputs, PortfolioUnlinkedRuns, \ - CSTInputs, CSTOutputs, HotSensibleTesInputs, HotSensibleTesOutputs + CSTInputs, CSTOutputs, HighTempThermalStorageInputs, HighTempThermalStorageOutputs import os import requests @@ -71,7 +71,7 @@ def help(request): d["ExistingBoiler"] = ExistingBoilerInputs.info_dict(ExistingBoilerInputs) d["Boiler"] = BoilerInputs.info_dict(BoilerInputs) d["HotThermalStorage"] = HotThermalStorageInputs.info_dict(HotThermalStorageInputs) - d["HotSensibleTes"] = HotSensibleTesInputs.info_dict(HotSensibleTesInputs) + d["HighTempThermalStorage"] = HighTempThermalStorageInputs.info_dict(HighTempThermalStorageInputs) d["ColdThermalStorage"] = ColdThermalStorageInputs.info_dict(ColdThermalStorageInputs) d["SpaceHeatingLoad"] = SpaceHeatingLoadInputs.info_dict(SpaceHeatingLoadInputs) d["DomesticHotWaterLoad"] = DomesticHotWaterLoadInputs.info_dict(DomesticHotWaterLoadInputs) @@ -124,7 +124,7 @@ def outputs(request): d["ExistingBoiler"] = ExistingBoilerOutputs.info_dict(ExistingBoilerOutputs) d["Boiler"] = BoilerOutputs.info_dict(BoilerOutputs) d["HotThermalStorage"] = HotThermalStorageOutputs.info_dict(HotThermalStorageOutputs) - d["HotSensibleTes"] = HotSensibleTesOutputs.info_dict(HotSensibleTesOutputs) + d["HighTempThermalStorage"] = HighTempThermalStorageOutputs.info_dict(HighTempThermalStorageOutputs) d["ColdThermalStorage"] = ColdThermalStorageOutputs.info_dict(ColdThermalStorageOutputs) d["Site"] = SiteOutputs.info_dict(SiteOutputs) d["HeatingLoad"] = HeatingLoadOutputs.info_dict(HeatingLoadOutputs) @@ -228,7 +228,7 @@ def results(request, run_uuid): try: r["inputs"]["ExistingChiller"] = meta.ExistingChillerInputs.dict except: pass - + try: r["inputs"]["ExistingBoiler"] = meta.ExistingBoilerInputs.dict except: pass @@ -238,7 +238,7 @@ def results(request, run_uuid): try: r["inputs"]["HotThermalStorage"] = meta.HotThermalStorageInputs.dict except: pass - try: r["inputs"]["HotSensibleTes"] = meta.HotSensibleTesInputs.dict + try: r["inputs"]["HighTempThermalStorage"] = meta.HighTempThermalStorageInputs.dict except: pass try: r["inputs"]["ColdThermalStorage"] = meta.ColdThermalStorageInputs.dict @@ -335,7 +335,7 @@ def results(request, run_uuid): try: r["outputs"]["HotThermalStorage"] = meta.HotThermalStorageOutputs.dict except: pass - try: r["outputs"]["HotSensibleTes"] = meta.HotSensibleTesOutputs.dict + try: r["outputs"]["HighTempThermalStorage"] = meta.HighTempThermalStorageOutputs.dict except: pass try: r["outputs"]["ColdThermalStorage"] = meta.ColdThermalStorageOutputs.dict except: pass From fd7ef3036ef5d6afe88ebd3ba994eae118c75589 Mon Sep 17 00:00:00 2001 From: Kadlec Date: Fri, 29 Aug 2025 13:31:58 -0600 Subject: [PATCH 31/57] renaming hot_sensible_tes to high_temp_thermal_storage in outputs --- ...our_cstoutputs_thermal_to_high_temp_the.py | 28 +++++++++++++++++++ reoptjl/models.py | 7 ++--- 2 files changed, 31 insertions(+), 4 deletions(-) create mode 100644 reoptjl/migrations/0101_rename_thermal_to_hot_sensible_tes_storage_series_mmbtu_per_hour_cstoutputs_thermal_to_high_temp_the.py diff --git a/reoptjl/migrations/0101_rename_thermal_to_hot_sensible_tes_storage_series_mmbtu_per_hour_cstoutputs_thermal_to_high_temp_the.py b/reoptjl/migrations/0101_rename_thermal_to_hot_sensible_tes_storage_series_mmbtu_per_hour_cstoutputs_thermal_to_high_temp_the.py new file mode 100644 index 000000000..990096014 --- /dev/null +++ b/reoptjl/migrations/0101_rename_thermal_to_hot_sensible_tes_storage_series_mmbtu_per_hour_cstoutputs_thermal_to_high_temp_the.py @@ -0,0 +1,28 @@ +# Generated by Django 4.0.7 on 2025-08-29 19:29 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('reoptjl', '0100_hightempthermalstorageinputs_and_more'), + ] + + operations = [ + migrations.RenameField( + model_name='cstoutputs', + old_name='thermal_to_hot_sensible_tes_storage_series_mmbtu_per_hour', + new_name='thermal_to_high_temp_thermal_storage_series_mmbtu_per_hour', + ), + migrations.RenameField( + model_name='electricheateroutputs', + old_name='thermal_to_hot_sensible_tes_series_mmbtu_per_hour', + new_name='thermal_to_high_temp_thermal_storage_series_mmbtu_per_hour', + ), + migrations.RenameField( + model_name='steamturbineoutputs', + old_name='thermal_to_hot_sensible_tes_series_mmbtu_per_hour', + new_name='thermal_to_high_temp_thermal_storage_series_mmbtu_per_hour', + ), + ] diff --git a/reoptjl/models.py b/reoptjl/models.py index 1fb39ede3..cc9d7c015 100644 --- a/reoptjl/models.py +++ b/reoptjl/models.py @@ -5617,7 +5617,7 @@ class ElectricHeaterOutputs(BaseModel, models.Model): default = list ) - thermal_to_hot_sensible_tes_series_mmbtu_per_hour = ArrayField( + thermal_to_high_temp_thermal_storage_series_mmbtu_per_hour = ArrayField( models.FloatField(null=True, blank=True), default = list ) @@ -6797,7 +6797,7 @@ class SteamTurbineOutputs(BaseModel, models.Model): default = list ) - thermal_to_hot_sensible_tes_series_mmbtu_per_hour = ArrayField( + thermal_to_high_temp_thermal_storage_series_mmbtu_per_hour = ArrayField( models.FloatField(null=True, blank=True), blank=True, default=list ) @@ -9021,8 +9021,7 @@ class CSTOutputs(BaseModel, models.Model): models.FloatField(null=True, blank=True), blank=True, default=list ) - # Should this be changed? - thermal_to_hot_sensible_tes_storage_series_mmbtu_per_hour = ArrayField( + thermal_to_high_temp_thermal_storage_series_mmbtu_per_hour = ArrayField( models.FloatField(null=True, blank=True), blank=True, default=list ) From c96b81ed23f1dacc9f07e83a9a2b7f99c7e1e172 Mon Sep 17 00:00:00 2001 From: Kadlec Date: Mon, 1 Sep 2025 15:43:24 -0600 Subject: [PATCH 32/57] updating version of REopt.jl --- julia_src/Manifest.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/julia_src/Manifest.toml b/julia_src/Manifest.toml index c37a6dff7..1b73dcec4 100644 --- a/julia_src/Manifest.toml +++ b/julia_src/Manifest.toml @@ -932,7 +932,7 @@ uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" [[deps.REopt]] deps = ["ArchGDAL", "CSV", "CoolProp", "DataFrames", "Dates", "DelimitedFiles", "HTTP", "JLD", "JSON", "JuMP", "LinDistFlow", "LinearAlgebra", "Logging", "MathOptInterface", "Requires", "Roots", "Statistics", "TestEnv"] -git-tree-sha1 = "a839010c66391e5aab327bd39a2d6f0bd255a275" +git-tree-sha1 = "ad1484650232c00b97841d2186820ee0b5abd3b0" repo-rev = "add-cst-reopt" repo-url = "https://github.com/NREL/REopt.jl.git" uuid = "d36ad4e8-d74a-4f7a-ace1-eaea049febf6" From 84c0c4f02cc48d24486b51e6359091bff3d25771 Mon Sep 17 00:00:00 2001 From: Kadlec Date: Mon, 1 Sep 2025 16:37:52 -0600 Subject: [PATCH 33/57] adding size_mmbtu_per_hour to cst outputs --- .../0102_cstoutputs_size_mmbtu_per_hour.py | 19 +++++++++++++++++++ reoptjl/models.py | 4 ++++ 2 files changed, 23 insertions(+) create mode 100644 reoptjl/migrations/0102_cstoutputs_size_mmbtu_per_hour.py diff --git a/reoptjl/migrations/0102_cstoutputs_size_mmbtu_per_hour.py b/reoptjl/migrations/0102_cstoutputs_size_mmbtu_per_hour.py new file mode 100644 index 000000000..07732ae95 --- /dev/null +++ b/reoptjl/migrations/0102_cstoutputs_size_mmbtu_per_hour.py @@ -0,0 +1,19 @@ +# Generated by Django 4.0.7 on 2025-09-01 22:23 + +import django.contrib.postgres.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('reoptjl', '0101_rename_thermal_to_hot_sensible_tes_storage_series_mmbtu_per_hour_cstoutputs_thermal_to_high_temp_the'), + ] + + operations = [ + migrations.AddField( + model_name='cstoutputs', + name='size_mmbtu_per_hour', + field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), blank=True, default=list, size=None), + ), + ] diff --git a/reoptjl/models.py b/reoptjl/models.py index cc9d7c015..9c4cd549b 100644 --- a/reoptjl/models.py +++ b/reoptjl/models.py @@ -9009,6 +9009,10 @@ class CSTOutputs(BaseModel, models.Model): size_kw = models.FloatField(null=True, blank=True) annual_electric_consumption_kwh = models.FloatField(null=True, blank=True) annual_thermal_production_mmbtu = models.FloatField(null=True, blank=True) + size_mmbtu_per_hour = ArrayField( + models.FloatField(null=True, blank=True), + default=list, blank=True + ) thermal_production_series_mmbtu_per_hour = ArrayField( models.FloatField(null=True, blank=True), default=list, blank=True From 7887f39f406efd530ff8f958a4d319dfe5c184da Mon Sep 17 00:00:00 2001 From: Kadlec Date: Tue, 2 Sep 2025 11:03:17 -0600 Subject: [PATCH 34/57] changing size_mmbtu_per_hour to float field --- ...> 0102_cstoutputs_size_mmbtu_per_hour_and_more.py} | 10 +++++++--- reoptjl/models.py | 11 +++++++---- 2 files changed, 14 insertions(+), 7 deletions(-) rename reoptjl/migrations/{0102_cstoutputs_size_mmbtu_per_hour.py => 0102_cstoutputs_size_mmbtu_per_hour_and_more.py} (51%) diff --git a/reoptjl/migrations/0102_cstoutputs_size_mmbtu_per_hour.py b/reoptjl/migrations/0102_cstoutputs_size_mmbtu_per_hour_and_more.py similarity index 51% rename from reoptjl/migrations/0102_cstoutputs_size_mmbtu_per_hour.py rename to reoptjl/migrations/0102_cstoutputs_size_mmbtu_per_hour_and_more.py index 07732ae95..6dc9d59f9 100644 --- a/reoptjl/migrations/0102_cstoutputs_size_mmbtu_per_hour.py +++ b/reoptjl/migrations/0102_cstoutputs_size_mmbtu_per_hour_and_more.py @@ -1,6 +1,5 @@ -# Generated by Django 4.0.7 on 2025-09-01 22:23 +# Generated by Django 4.0.7 on 2025-09-02 15:58 -import django.contrib.postgres.fields from django.db import migrations, models @@ -14,6 +13,11 @@ class Migration(migrations.Migration): migrations.AddField( model_name='cstoutputs', name='size_mmbtu_per_hour', - field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), blank=True, default=list, size=None), + field=models.FloatField(blank=True, null=True), + ), + migrations.AddField( + model_name='hightempthermalstorageinputs', + name='fluid', + field=models.TextField(blank=True, default='INCOMP::NaK', help_text='Type of Fluid for High Temp Thermal Storage System'), ), ] diff --git a/reoptjl/models.py b/reoptjl/models.py index 9c4cd549b..965e425ac 100644 --- a/reoptjl/models.py +++ b/reoptjl/models.py @@ -7021,6 +7021,12 @@ class HighTempThermalStorageInputs(BaseModel, models.Model): primary_key=True ) + fluid =models.TextField( + blank=True, + default="INCOMP::NaK", + help_text="Type of Fluid for High Temp Thermal Storage System" + ) + min_kwh = models.FloatField( validators=[ MinValueValidator(0), @@ -9007,12 +9013,9 @@ class CSTOutputs(BaseModel, models.Model): unique=False ) size_kw = models.FloatField(null=True, blank=True) + size_mmbtu_per_hour = models.FloatField(null=True, blank=True) annual_electric_consumption_kwh = models.FloatField(null=True, blank=True) annual_thermal_production_mmbtu = models.FloatField(null=True, blank=True) - size_mmbtu_per_hour = ArrayField( - models.FloatField(null=True, blank=True), - default=list, blank=True - ) thermal_production_series_mmbtu_per_hour = ArrayField( models.FloatField(null=True, blank=True), default=list, blank=True From 61ca69b750f046f5f6781402ab259f494c1d2a96 Mon Sep 17 00:00:00 2001 From: Kadlec Date: Tue, 2 Sep 2025 11:15:05 -0600 Subject: [PATCH 35/57] debugging deploy to api --- .../0102_cstoutputs_size_mmbtu_per_hour.py | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 reoptjl/migrations/0102_cstoutputs_size_mmbtu_per_hour.py diff --git a/reoptjl/migrations/0102_cstoutputs_size_mmbtu_per_hour.py b/reoptjl/migrations/0102_cstoutputs_size_mmbtu_per_hour.py new file mode 100644 index 000000000..6dc9d59f9 --- /dev/null +++ b/reoptjl/migrations/0102_cstoutputs_size_mmbtu_per_hour.py @@ -0,0 +1,23 @@ +# Generated by Django 4.0.7 on 2025-09-02 15:58 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('reoptjl', '0101_rename_thermal_to_hot_sensible_tes_storage_series_mmbtu_per_hour_cstoutputs_thermal_to_high_temp_the'), + ] + + operations = [ + migrations.AddField( + model_name='cstoutputs', + name='size_mmbtu_per_hour', + field=models.FloatField(blank=True, null=True), + ), + migrations.AddField( + model_name='hightempthermalstorageinputs', + name='fluid', + field=models.TextField(blank=True, default='INCOMP::NaK', help_text='Type of Fluid for High Temp Thermal Storage System'), + ), + ] From 36b199c8638ad8eacbc95a198ad54ca0b69a98e8 Mon Sep 17 00:00:00 2001 From: Kadlec Date: Tue, 2 Sep 2025 11:16:20 -0600 Subject: [PATCH 36/57] debugging deploy to api --- ...cstoutputs_size_mmbtu_per_hour_and_more.py | 23 ------------------- 1 file changed, 23 deletions(-) delete mode 100644 reoptjl/migrations/0102_cstoutputs_size_mmbtu_per_hour_and_more.py diff --git a/reoptjl/migrations/0102_cstoutputs_size_mmbtu_per_hour_and_more.py b/reoptjl/migrations/0102_cstoutputs_size_mmbtu_per_hour_and_more.py deleted file mode 100644 index 6dc9d59f9..000000000 --- a/reoptjl/migrations/0102_cstoutputs_size_mmbtu_per_hour_and_more.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 4.0.7 on 2025-09-02 15:58 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('reoptjl', '0101_rename_thermal_to_hot_sensible_tes_storage_series_mmbtu_per_hour_cstoutputs_thermal_to_high_temp_the'), - ] - - operations = [ - migrations.AddField( - model_name='cstoutputs', - name='size_mmbtu_per_hour', - field=models.FloatField(blank=True, null=True), - ), - migrations.AddField( - model_name='hightempthermalstorageinputs', - name='fluid', - field=models.TextField(blank=True, default='INCOMP::NaK', help_text='Type of Fluid for High Temp Thermal Storage System'), - ), - ] From 533b6547cb2cfe5ee9f3f0b70367ec0a58f02046 Mon Sep 17 00:00:00 2001 From: Kadlec Date: Tue, 2 Sep 2025 11:25:56 -0600 Subject: [PATCH 37/57] debugging deploy to api --- .../0102_cstoutputs_size_mmbtu_per_hour.py | 10 +++------- reoptjl/models.py | 12 +++++------- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/reoptjl/migrations/0102_cstoutputs_size_mmbtu_per_hour.py b/reoptjl/migrations/0102_cstoutputs_size_mmbtu_per_hour.py index 6dc9d59f9..0ec7c476a 100644 --- a/reoptjl/migrations/0102_cstoutputs_size_mmbtu_per_hour.py +++ b/reoptjl/migrations/0102_cstoutputs_size_mmbtu_per_hour.py @@ -1,5 +1,6 @@ -# Generated by Django 4.0.7 on 2025-09-02 15:58 +# Generated by Django 4.0.7 on 2025-09-02 17:25 +import django.contrib.postgres.fields from django.db import migrations, models @@ -13,11 +14,6 @@ class Migration(migrations.Migration): migrations.AddField( model_name='cstoutputs', name='size_mmbtu_per_hour', - field=models.FloatField(blank=True, null=True), - ), - migrations.AddField( - model_name='hightempthermalstorageinputs', - name='fluid', - field=models.TextField(blank=True, default='INCOMP::NaK', help_text='Type of Fluid for High Temp Thermal Storage System'), + field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), blank=True, default=list, size=None), ), ] diff --git a/reoptjl/models.py b/reoptjl/models.py index 965e425ac..338477e5e 100644 --- a/reoptjl/models.py +++ b/reoptjl/models.py @@ -7021,12 +7021,6 @@ class HighTempThermalStorageInputs(BaseModel, models.Model): primary_key=True ) - fluid =models.TextField( - blank=True, - default="INCOMP::NaK", - help_text="Type of Fluid for High Temp Thermal Storage System" - ) - min_kwh = models.FloatField( validators=[ MinValueValidator(0), @@ -9013,9 +9007,13 @@ class CSTOutputs(BaseModel, models.Model): unique=False ) size_kw = models.FloatField(null=True, blank=True) - size_mmbtu_per_hour = models.FloatField(null=True, blank=True) + # size_mmbtu_per_hour = models.FloatField(null=True, blank=True) annual_electric_consumption_kwh = models.FloatField(null=True, blank=True) annual_thermal_production_mmbtu = models.FloatField(null=True, blank=True) + size_mmbtu_per_hour = ArrayField( + models.FloatField(null=True, blank=True), + default=list, blank=True + ) thermal_production_series_mmbtu_per_hour = ArrayField( models.FloatField(null=True, blank=True), default=list, blank=True From 22759f9ca6fff20d0751940874a994051e749b7b Mon Sep 17 00:00:00 2001 From: Kadlec Date: Tue, 2 Sep 2025 12:16:28 -0600 Subject: [PATCH 38/57] combining migrations --- ...0_hightempthermalstorageinputs_and_more.py | 25 +++++++++++++++-- ...our_cstoutputs_thermal_to_high_temp_the.py | 28 ------------------- .../0102_cstoutputs_size_mmbtu_per_hour.py | 19 ------------- reoptjl/models.py | 11 ++++---- 4 files changed, 29 insertions(+), 54 deletions(-) delete mode 100644 reoptjl/migrations/0101_rename_thermal_to_hot_sensible_tes_storage_series_mmbtu_per_hour_cstoutputs_thermal_to_high_temp_the.py delete mode 100644 reoptjl/migrations/0102_cstoutputs_size_mmbtu_per_hour.py diff --git a/reoptjl/migrations/0100_hightempthermalstorageinputs_and_more.py b/reoptjl/migrations/0100_hightempthermalstorageinputs_and_more.py index a5ded9368..979aa50d1 100644 --- a/reoptjl/migrations/0100_hightempthermalstorageinputs_and_more.py +++ b/reoptjl/migrations/0100_hightempthermalstorageinputs_and_more.py @@ -1,4 +1,4 @@ -# Generated by Django 4.0.7 on 2025-08-29 18:16 +# Generated by Django 4.0.7 on 2025-09-02 18:10 import django.contrib.postgres.fields import django.core.validators @@ -18,6 +18,7 @@ class Migration(migrations.Migration): name='HighTempThermalStorageInputs', fields=[ ('meta', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, related_name='HighTempThermalStorageInputs', serialize=False, to='reoptjl.apimeta')), + ('fluid', models.TextField(blank=True, default='INCOMP::NaK', help_text='Type of fluid for your High Temp Thermal Storage system')), ('min_kwh', models.FloatField(blank=True, default=0.0, help_text='Minimum TES volume (energy) size constraint for optimization', null=True, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1000000000.0)])), ('max_kwh', models.FloatField(blank=True, default=0.0, help_text='Maximum TES volume (energy) size constraint for optimization. Set to zero to disable storage', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1000000000.0)])), ('hot_temp_degF', models.FloatField(blank=True, default=1065.0, help_text='Hot-side supply water temperature from HotTES (top of tank) to the heating load', validators=[django.core.validators.MinValueValidator(200.0), django.core.validators.MaxValueValidator(2000.0)])), @@ -58,19 +59,39 @@ class Migration(migrations.Migration): model_name='hotsensibletesoutputs', name='meta', ), + migrations.RenameField( + model_name='cstoutputs', + old_name='thermal_to_hot_sensible_tes_series_mmbtu_per_hour', + new_name='thermal_to_high_temp_thermal_storage_series_mmbtu_per_hour', + ), + migrations.RenameField( + model_name='electricheateroutputs', + old_name='thermal_to_hot_sensible_tes_series_mmbtu_per_hour', + new_name='thermal_to_high_temp_thermal_storage_series_mmbtu_per_hour', + ), + migrations.RenameField( + model_name='steamturbineoutputs', + old_name='thermal_to_hot_sensible_tes_series_mmbtu_per_hour', + new_name='thermal_to_high_temp_thermal_storage_series_mmbtu_per_hour', + ), migrations.RemoveField( model_name='cstinputs', name='capacity_factor_series', ), migrations.RemoveField( model_name='cstoutputs', - name='thermal_to_hot_sensible_tes_series_mmbtu_per_hour', + name='thermal_to_hot_sensible_tes_storage_series_mmbtu_per_hour', ), migrations.AddField( model_name='cstinputs', name='production_factor', field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True), blank=True, default=list, help_text='Optional user-defined production factors for CST.', size=None), ), + migrations.AddField( + model_name='cstoutputs', + name='size_mmbtu_per_hour', + field=models.FloatField(blank=True, null=True), + ), migrations.DeleteModel( name='HotSensibleTESInputs', ), diff --git a/reoptjl/migrations/0101_rename_thermal_to_hot_sensible_tes_storage_series_mmbtu_per_hour_cstoutputs_thermal_to_high_temp_the.py b/reoptjl/migrations/0101_rename_thermal_to_hot_sensible_tes_storage_series_mmbtu_per_hour_cstoutputs_thermal_to_high_temp_the.py deleted file mode 100644 index 990096014..000000000 --- a/reoptjl/migrations/0101_rename_thermal_to_hot_sensible_tes_storage_series_mmbtu_per_hour_cstoutputs_thermal_to_high_temp_the.py +++ /dev/null @@ -1,28 +0,0 @@ -# Generated by Django 4.0.7 on 2025-08-29 19:29 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('reoptjl', '0100_hightempthermalstorageinputs_and_more'), - ] - - operations = [ - migrations.RenameField( - model_name='cstoutputs', - old_name='thermal_to_hot_sensible_tes_storage_series_mmbtu_per_hour', - new_name='thermal_to_high_temp_thermal_storage_series_mmbtu_per_hour', - ), - migrations.RenameField( - model_name='electricheateroutputs', - old_name='thermal_to_hot_sensible_tes_series_mmbtu_per_hour', - new_name='thermal_to_high_temp_thermal_storage_series_mmbtu_per_hour', - ), - migrations.RenameField( - model_name='steamturbineoutputs', - old_name='thermal_to_hot_sensible_tes_series_mmbtu_per_hour', - new_name='thermal_to_high_temp_thermal_storage_series_mmbtu_per_hour', - ), - ] diff --git a/reoptjl/migrations/0102_cstoutputs_size_mmbtu_per_hour.py b/reoptjl/migrations/0102_cstoutputs_size_mmbtu_per_hour.py deleted file mode 100644 index 0ec7c476a..000000000 --- a/reoptjl/migrations/0102_cstoutputs_size_mmbtu_per_hour.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 4.0.7 on 2025-09-02 17:25 - -import django.contrib.postgres.fields -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('reoptjl', '0101_rename_thermal_to_hot_sensible_tes_storage_series_mmbtu_per_hour_cstoutputs_thermal_to_high_temp_the'), - ] - - operations = [ - migrations.AddField( - model_name='cstoutputs', - name='size_mmbtu_per_hour', - field=django.contrib.postgres.fields.ArrayField(base_field=models.FloatField(blank=True, null=True), blank=True, default=list, size=None), - ), - ] diff --git a/reoptjl/models.py b/reoptjl/models.py index 338477e5e..b0101aeab 100644 --- a/reoptjl/models.py +++ b/reoptjl/models.py @@ -7020,6 +7020,11 @@ class HighTempThermalStorageInputs(BaseModel, models.Model): related_name="HighTempThermalStorageInputs", primary_key=True ) + fluid = models.TextField( + blank=True, + default="INCOMP::NaK", + help_text="Type of fluid for your High Temp Thermal Storage system" + ) min_kwh = models.FloatField( validators=[ @@ -9007,13 +9012,9 @@ class CSTOutputs(BaseModel, models.Model): unique=False ) size_kw = models.FloatField(null=True, blank=True) - # size_mmbtu_per_hour = models.FloatField(null=True, blank=True) + size_mmbtu_per_hour = models.FloatField(null=True, blank=True) annual_electric_consumption_kwh = models.FloatField(null=True, blank=True) annual_thermal_production_mmbtu = models.FloatField(null=True, blank=True) - size_mmbtu_per_hour = ArrayField( - models.FloatField(null=True, blank=True), - default=list, blank=True - ) thermal_production_series_mmbtu_per_hour = ArrayField( models.FloatField(null=True, blank=True), default=list, blank=True From 7b2bf06d3e7afccf9cb866a6930668a5fa0a66fd Mon Sep 17 00:00:00 2001 From: adfarth Date: Tue, 2 Sep 2025 16:15:39 -0600 Subject: [PATCH 39/57] CHP and GHP MACRS --- CHANGELOG.md | 2 ++ reoptjl/models.py | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bb8f07c56..07d2a7d64 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,8 @@ Update the following inputs from the previous --> new values: - `ColdThermalStorage`, `HotThermalStorage`, `ElectricStorage` `macrs_option_years` 7 --> 5 - `ColdThermalStorage`, `HotThermalStorage`, `ElectricStorage`, `PV`, `Wind` `macrs_bonus_fraction` 0.6 --> 1.0 - `CHP.federal_itc_fraction`: 0.3 --> 0.0 +- `CHP.macrs_bonus_fraction` and `GHP.macrs_bonus_fraction`: 0.4 --> 0.0 +- `CHP.macrs_option_years` and `GHP.macrs_option_years`: 5 --> 0 - `Wind.om_cost_per_kw`: 36.0 --> 42.0 - `Wind.size_class_to_installed_cost` = Dict( "residential"=> 6339.0, --> 7692.0 diff --git a/reoptjl/models.py b/reoptjl/models.py index ac991675b..2f203843e 100644 --- a/reoptjl/models.py +++ b/reoptjl/models.py @@ -4432,13 +4432,13 @@ class CHPInputs(BaseModel, models.Model): #Financial and emissions macrs_option_years = models.IntegerField( - default=MACRS_YEARS_CHOICES.FIVE, + default=MACRS_YEARS_CHOICES.ZERO, choices=MACRS_YEARS_CHOICES.choices, blank=True, help_text="Duration over which accelerated depreciation will occur. Set to zero to disable" ) macrs_bonus_fraction = models.FloatField( - default=0.4, + default=0.0, validators=[ MinValueValidator(0), MaxValueValidator(1) @@ -8399,13 +8399,13 @@ class GHPInputs(BaseModel, models.Model): ) macrs_option_years = models.IntegerField( - default=MACRS_YEARS_CHOICES.FIVE, + default=MACRS_YEARS_CHOICES.ZERO, choices=MACRS_YEARS_CHOICES.choices, blank=True, help_text="Duration over which accelerated depreciation will occur. Set to zero to disable" ) macrs_bonus_fraction = models.FloatField( - default=0.4, + default=0.0, validators=[ MinValueValidator(0), MaxValueValidator(1) From 5e465813e3db85ef28b41e4cff952c9123bce55b Mon Sep 17 00:00:00 2001 From: Kadlec Date: Wed, 3 Sep 2025 08:26:23 -0600 Subject: [PATCH 40/57] adding cst and high temp tes to run summary --- reoptjl/views.py | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/reoptjl/views.py b/reoptjl/views.py index 8d7cd4ba4..e00a77660 100644 --- a/reoptjl/views.py +++ b/reoptjl/views.py @@ -959,7 +959,9 @@ def summary(request, user_uuid): "wind_kw", # Wind Size (kW) "gen_kw", # Generator Size (kW) "batt_kw", # Battery Power (kW) - "batt_kwh" # Battery Capacity (kWh) + "batt_kwh", # Battery Capacity (kWh) + "cst_kw", # CST Size (kW) + "hightemptes_kwh" # High Temp TES Capacity (kWh) "" }] } @@ -1103,7 +1105,7 @@ def summary_by_chunk(request, user_uuid, chunk): def create_summary_dict(user_uuid:str,summary_dict:dict): # if these keys are missing from a `scenario` we add 0s for them, all Floats. - optional_keys = ["npv_us_dollars", "net_capital_costs", "year_one_savings_us_dollars", "pv_kw", "wind_kw", "gen_kw", "batt_kw", "batt_kwh"] + optional_keys = ["npv_us_dollars", "net_capital_costs", "year_one_savings_us_dollars", "pv_kw", "wind_kw", "gen_kw", "batt_kw", "batt_kwh", "cst_kw", "hightemptes_kwh"] # Create eventual response dictionary return_dict = dict() @@ -1324,6 +1326,22 @@ def queryset_for_summary(api_metas,summary_dict:dict): for m in gen: summary_dict[str(m.meta.run_uuid)]['gen_kw'] = m.size_kw + cst = CSTOutputs.objects.filter(meta__run_uuid__in=run_uuids).only( + 'meta__run_uuid', + 'size_kw' + ) + if len(cst) > 0: + for m in cst: + summary_dict[str(m.meta.run_uuid)]['cst_kw'] = m.size_kw + + hightemptes = HighTempThermalStorageOutputs.objects.filter(meta__run_uuid__in=run_uuids).only( + 'meta__run_uuid', + 'size_kwh' + ) + if len(hightemptes) > 0: + for m in hightemptes: + summary_dict[str(m.meta.run_uuid)]['hightemptes_kwh'] = m.size_kwh + # assumes run_uuids exist in both CHPInputs and CHPOutputs chpInputs = CHPInputs.objects.filter(meta__run_uuid__in=run_uuids).only( 'meta__run_uuid', From dd7c320664af0b224b47b69c3db65a3f165b4d87 Mon Sep 17 00:00:00 2001 From: adfarth Date: Wed, 3 Sep 2025 10:21:02 -0600 Subject: [PATCH 41/57] Create 0093_alter_chpinputs_federal_itc_fraction_and_more.py --- ...chpinputs_federal_itc_fraction_and_more.py | 124 ++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 reoptjl/migrations/0093_alter_chpinputs_federal_itc_fraction_and_more.py diff --git a/reoptjl/migrations/0093_alter_chpinputs_federal_itc_fraction_and_more.py b/reoptjl/migrations/0093_alter_chpinputs_federal_itc_fraction_and_more.py new file mode 100644 index 000000000..354937af6 --- /dev/null +++ b/reoptjl/migrations/0093_alter_chpinputs_federal_itc_fraction_and_more.py @@ -0,0 +1,124 @@ +# Generated by Django 4.0.7 on 2025-09-03 16:20 + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('reoptjl', '0092_merge_20250613_0525'), + ] + + operations = [ + migrations.AlterField( + model_name='chpinputs', + name='federal_itc_fraction', + field=models.FloatField(blank=True, default=0.0, help_text='Percentage of capital costs that are credited towards federal taxes', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1)]), + ), + migrations.AlterField( + model_name='chpinputs', + name='macrs_bonus_fraction', + field=models.FloatField(blank=True, default=0.0, help_text='Percent of upfront project costs to depreciate in year one in addition to scheduled depreciation', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1)]), + ), + migrations.AlterField( + model_name='chpinputs', + name='macrs_option_years', + field=models.IntegerField(blank=True, choices=[(0, 'Zero'), (5, 'Five'), (7, 'Seven')], default=0, help_text='Duration over which accelerated depreciation will occur. Set to zero to disable'), + ), + migrations.AlterField( + model_name='coldthermalstorageinputs', + name='macrs_bonus_fraction', + field=models.FloatField(blank=True, default=1.0, help_text='Percent of upfront project costs to depreciate in year one in addition to scheduled depreciation', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1)]), + ), + migrations.AlterField( + model_name='coldthermalstorageinputs', + name='macrs_option_years', + field=models.IntegerField(blank=True, choices=[(0, 'Zero'), (5, 'Five'), (7, 'Seven')], default=5, help_text='Duration over which accelerated depreciation will occur. Set to zero to disable'), + ), + migrations.AlterField( + model_name='electricstorageinputs', + name='macrs_bonus_fraction', + field=models.FloatField(blank=True, default=1.0, help_text='Percent of upfront project costs to depreciate in year one in addition to scheduled depreciation', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1)]), + ), + migrations.AlterField( + model_name='electricstorageinputs', + name='macrs_option_years', + field=models.IntegerField(blank=True, choices=[(0, 'Zero'), (5, 'Five'), (7, 'Seven')], default=5, help_text='Duration over which accelerated depreciation will occur. Set to zero to disable'), + ), + migrations.AlterField( + model_name='financialinputs', + name='boiler_fuel_cost_escalation_rate_fraction', + field=models.FloatField(blank=True, default=0.0348, help_text='Annual nominal boiler fuel cost escalation rate', validators=[django.core.validators.MinValueValidator(-1), django.core.validators.MaxValueValidator(1)]), + ), + migrations.AlterField( + model_name='financialinputs', + name='chp_fuel_cost_escalation_rate_fraction', + field=models.FloatField(blank=True, default=0.0348, help_text='Annual nominal chp fuel cost escalation rate', validators=[django.core.validators.MinValueValidator(-1), django.core.validators.MaxValueValidator(1)]), + ), + migrations.AlterField( + model_name='financialinputs', + name='elec_cost_escalation_rate_fraction', + field=models.FloatField(blank=True, default=0.0166, help_text='Annual nominal utility electricity cost escalation rate.', validators=[django.core.validators.MinValueValidator(-1), django.core.validators.MaxValueValidator(1)]), + ), + migrations.AlterField( + model_name='financialinputs', + name='existing_boiler_fuel_cost_escalation_rate_fraction', + field=models.FloatField(blank=True, default=0.0348, help_text='Annual nominal existing boiler fuel cost escalation rate', validators=[django.core.validators.MinValueValidator(-1), django.core.validators.MaxValueValidator(1)]), + ), + migrations.AlterField( + model_name='financialinputs', + name='generator_fuel_cost_escalation_rate_fraction', + field=models.FloatField(blank=True, default=0.0197, help_text='Annual nominal boiler fuel cost escalation rate', validators=[django.core.validators.MinValueValidator(-1), django.core.validators.MaxValueValidator(1)]), + ), + migrations.AlterField( + model_name='financialinputs', + name='offtaker_discount_rate_fraction', + field=models.FloatField(blank=True, default=0.0624, help_text='Nominal energy offtaker discount rate. In single ownership model the offtaker is also the generation owner.', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1)]), + ), + migrations.AlterField( + model_name='financialinputs', + name='owner_discount_rate_fraction', + field=models.FloatField(blank=True, default=0.0624, help_text='Nominal generation owner discount rate. Used for two party financing model. In two party ownership model the offtaker does not own the generator(s).', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1)]), + ), + migrations.AlterField( + model_name='generatorinputs', + name='fuel_cost_per_gallon', + field=models.FloatField(blank=True, default=2.25, help_text='Diesel cost in $/gallon', validators=[django.core.validators.MinValueValidator(0.0), django.core.validators.MaxValueValidator(100.0)]), + ), + migrations.AlterField( + model_name='ghpinputs', + name='macrs_bonus_fraction', + field=models.FloatField(blank=True, default=0.0, help_text='Percent of upfront project costs to depreciate in year one in addition to scheduled depreciation', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1)]), + ), + migrations.AlterField( + model_name='ghpinputs', + name='macrs_option_years', + field=models.IntegerField(blank=True, choices=[(0, 'Zero'), (5, 'Five'), (7, 'Seven')], default=0, help_text='Duration over which accelerated depreciation will occur. Set to zero to disable'), + ), + migrations.AlterField( + model_name='hotthermalstorageinputs', + name='macrs_bonus_fraction', + field=models.FloatField(blank=True, default=1.0, help_text='Percent of upfront project costs to depreciate in year one in addition to scheduled depreciation', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1)]), + ), + migrations.AlterField( + model_name='hotthermalstorageinputs', + name='macrs_option_years', + field=models.IntegerField(blank=True, choices=[(0, 'Zero'), (5, 'Five'), (7, 'Seven')], default=5, help_text='Duration over which accelerated depreciation will occur. Set to zero to disable'), + ), + migrations.AlterField( + model_name='pvinputs', + name='macrs_bonus_fraction', + field=models.FloatField(blank=True, default=1.0, help_text='Percent of upfront project costs to depreciate in year one in addition to scheduled depreciation', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1)]), + ), + migrations.AlterField( + model_name='windinputs', + name='macrs_bonus_fraction', + field=models.FloatField(blank=True, default=1.0, help_text='Percent of upfront project costs to depreciate in year one in addition to scheduled depreciation', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1)]), + ), + migrations.AlterField( + model_name='windinputs', + name='om_cost_per_kw', + field=models.FloatField(blank=True, default=42, help_text='Annual operations and maintenance costs in $/kW', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1000.0)]), + ), + ] From 7420015553a614a88a63441bfc670ef173f31588 Mon Sep 17 00:00:00 2001 From: wbecker Date: Wed, 3 Sep 2025 22:09:28 -0600 Subject: [PATCH 42/57] Reduce dev and staging Julia CPU Request to 1 --- .helm/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.helm/values.yaml b/.helm/values.yaml index 2e227dfc3..385960575 100644 --- a/.helm/values.yaml +++ b/.helm/values.yaml @@ -19,7 +19,7 @@ celeryCpuLimit: "2000m" celeryMemoryRequest: "700Mi" celeryMemoryLimit: "700Mi" juliaReplicas: 1 -juliaCpuRequest: "2000m" +juliaCpuRequest: "1000m" juliaCpuLimit: "4000m" juliaMemoryRequest: "8000Mi" juliaMemoryLimit: "8000Mi" From cd2d0db04df2328deaafe0b67c51b6fc37c79c5b Mon Sep 17 00:00:00 2001 From: wbecker Date: Wed, 3 Sep 2025 22:18:53 -0600 Subject: [PATCH 43/57] Reduce Julia CPU Limit for dev and staging --- .helm/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.helm/values.yaml b/.helm/values.yaml index 385960575..7348a0da7 100644 --- a/.helm/values.yaml +++ b/.helm/values.yaml @@ -20,6 +20,6 @@ celeryMemoryRequest: "700Mi" celeryMemoryLimit: "700Mi" juliaReplicas: 1 juliaCpuRequest: "1000m" -juliaCpuLimit: "4000m" +juliaCpuLimit: "2000m" juliaMemoryRequest: "8000Mi" juliaMemoryLimit: "8000Mi" From 364ba89ab65feb8d34da436c9397576b50bfb483 Mon Sep 17 00:00:00 2001 From: Kadlec Date: Thu, 4 Sep 2025 09:52:31 -0600 Subject: [PATCH 44/57] updating download selected results spreadsheet --- reoptjl/custom_table_config.py | 60 ++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/reoptjl/custom_table_config.py b/reoptjl/custom_table_config.py index f12854ceb..0cf41dd05 100644 --- a/reoptjl/custom_table_config.py +++ b/reoptjl/custom_table_config.py @@ -234,6 +234,18 @@ "bau_value" : lambda df: safe_get(df, "outputs.GHP.ghpghx_chosen_outputs.length_boreholes_ft_bau")*safe_get(df, "outputs.GHP.ghpghx_chosen_outputs.number_of_boreholes_bau"), "scenario_value": lambda df: safe_get(df, "outputs.GHP.ghpghx_chosen_outputs.length_boreholes_ft")*safe_get(df, "outputs.GHP.ghpghx_chosen_outputs.number_of_boreholes") }, + { + "label" : "Concentrating Solar Thermal Capacity (kW)", + "key" : "cst_capacity", + "bau_value" : lambda df: safe_get(df, "outputs.CST.size_kw_bau"), + "scenario_value": lambda df: safe_get(df, "outputs.CST.size_kw") + }, + { + "label" : "High Temperature Thermal Storage Capacity (kWh)", + "key" : "high_temp_tes_capacity", + "bau_value" : lambda df: safe_get(df, "outputs.HighTempThermalStorage.size_kwh_bau"), + "scenario_value": lambda df: safe_get(df, "outputs.HighTempThermalStorage.size_kwh") + }, # New ASHP entries { "label" : "ASHP Space Heating and Cooling Capacity (ton)", @@ -846,6 +858,12 @@ "bau_value" : lambda df: safe_get(df, "outputs.SteamTurbine.annual_electric_production_kwh_bau"), "scenario_value": lambda df: safe_get(df, "outputs.SteamTurbine.annual_electric_production_kwh") }, + { + "label" : "CST Total Electricity Produced (kWh/yr)", + "key" : "cst_total_electricity_produced", + "bau_value" : lambda df: safe_get(df, "outputs.CST.annual_electric_production_kwh_bau"), + "scenario_value": lambda df: safe_get(df, "outputs.CST.annual_electric_production_kwh") + }, ##################################################################################################### ############################## Annual Heating Thermal Production ############################# ##################################################################################################### @@ -915,6 +933,12 @@ "bau_value" : lambda df: safe_get(df, "outputs.SteamTurbine.thermal_to_load_series_mmbtu_per_hour_bau"), "scenario_value": lambda df: safe_get(df, "outputs.SteamTurbine.thermal_to_load_series_mmbtu_per_hour") }, + { + "label" : "Steam Turbine Charging High Temp Thermal Storage (MMBtu/yr)", + "key" : "steam_turbine_charging_high_temp_thermal_storage", + "bau_value" : lambda df: safe_get(df, "outputs.SteamTurbine.thermal_to_high_temp_thermal_storage_series_mmbtu_per_hour_bau"), + "scenario_value": lambda df: safe_get(df, "outputs.SteamTurbine.thermal_to_high_temp_thermal_storage_series_mmbtu_per_hour") + }, { "label" : "Steam Turbine Charging Hot Water Storage (MMBtu/yr)", "key" : "steam_turbine_charging_hot_water_storage", @@ -963,6 +987,42 @@ "bau_value" : lambda df: safe_get(df, "outputs.ASHPWaterHeater.thermal_to_storage_series_mmbtu_per_hour_bau"), "scenario_value": lambda df: safe_get(df, "outputs.ASHPWaterHeater.thermal_to_storage_series_mmbtu_per_hour") }, + { + "label" : "CST Charging High Temp Thermal Storage (MMBtu/yr)", + "key" : "cst_charging_high_temp_thermal_storage", + "bau_value" : lambda df: safe_get(df, "outputs.CST.thermal_to_high_temp_thermal_series_mmbtu_per_hour_bau"), + "scenario_value": lambda df: safe_get(df, "outputs.CST.thermal_to_high_temp_thermal_series_mmbtu_per_hour") + }, + { + "label" : "CST Charging Hot Water Storage (MMBtu/yr)", + "key" : "cst_charging_hot_water_storage", + "bau_value" : lambda df: safe_get(df, "outputs.CST.thermal_to_storage_series_mmbtu_per_hour_bau"), + "scenario_value": lambda df: safe_get(df, "outputs.CST.thermal_to_storage_series_mmbtu_per_hour") + }, + { + "label" : "CST Thermal to Steam Turbine (MMBtu/yr)", + "key" : "cst_thermal_to_steam_turbine", + "bau_value" : lambda df: safe_get(df, "outputs.CST.thermal_to_steamturbine_series_mmbtu_per_hour_bau"), + "scenario_value": lambda df: safe_get(df, "outputs.CST.thermal_to_steamturbine_series_mmbtu_per_hour") + }, + { + "label" : "CST Thermal Vented (MMBtu/yr)", + "key" : "cst_thermal_vented", + "bau_value" : lambda df: safe_get(df, "outputs.CST.thermal_curtailed_series_mmbtu_per_hour_bau"), + "scenario_value": lambda df: safe_get(df, "outputs.CST.thermal_curtailed_series_mmbtu_per_hour") + }, + { + "label" : "CST Total Thermal Produced (MMBtu/yr)", + "key" : "cst_total_thermal_produced", + "bau_value" : lambda df: safe_get(df, "outputs.CST.annual_thermal_production_mmbtu_bau"), + "scenario_value": lambda df: safe_get(df, "outputs.CST.annual_thermal_production_mmbtu") + }, + { + "label" : "High Temp Thermal Storage Serving Thermal Load (MMBtu/yr)", + "key" : "high_temp_thermal_storage_serving_thermal_load", + "bau_value" : lambda df: safe_get(df, "outputs.HighTempThermalStorage.storage_to_load_series_mmbtu_per_hour_bau"), + "scenario_value": lambda df: safe_get(df, "outputs.HighTempThermalStorage.storage_to_load_series_mmbtu_per_hour") + }, { "label" : "Hot Water Storage Serving Thermal Load (MMBtu/yr)", "key" : "hot_water_storage_serving_thermal_load", From cfc9b22d806fa46025e8236f5a5ad82f830b86f3 Mon Sep 17 00:00:00 2001 From: adfarth Date: Fri, 5 Sep 2025 10:09:23 -0600 Subject: [PATCH 45/57] chp and steamturbine macrs --- CHANGELOG.md | 10 ++++++---- reoptjl/models.py | 8 ++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 07d2a7d64..09bc917a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,11 +38,13 @@ Update the following inputs from the previous --> new values: - `Financial.chp_fuel_cost_escalation_rate_fraction `: 0.015 --> 0.0348 - `Financial.generator_fuel_cost_escalation_rate_fraction `: 0.012 --> 0.0197 - `Generator.fuel_cost_per_gallon`: 3.61 --> 2.25 -- `ColdThermalStorage`, `HotThermalStorage`, `ElectricStorage` `macrs_option_years` 7 --> 5 -- `ColdThermalStorage`, `HotThermalStorage`, `ElectricStorage`, `PV`, `Wind` `macrs_bonus_fraction` 0.6 --> 1.0 +- `ColdThermalStorage`, `HotThermalStorage`, `ElectricStorage` `macrs_option_years`: 7 --> 5 +- `CHP`, `ColdThermalStorage`, `HotThermalStorage`, `ElectricStorage`, `PV`, `Wind` `macrs_bonus_fraction` 0.6 --> 1.0 +- `GHP.macrs_bonus_fraction`: 0.4 --> 0.0 +- `GHP.macrs_option_years`: 5 --> 0 +- `SteamTurbine.macrs_bonus_fraction`: 0 --> 1.0 +- `SteamTurbine.macrs_option_years`: 0 --> 5 (in order for 100% bonus depr to apply) - `CHP.federal_itc_fraction`: 0.3 --> 0.0 -- `CHP.macrs_bonus_fraction` and `GHP.macrs_bonus_fraction`: 0.4 --> 0.0 -- `CHP.macrs_option_years` and `GHP.macrs_option_years`: 5 --> 0 - `Wind.om_cost_per_kw`: 36.0 --> 42.0 - `Wind.size_class_to_installed_cost` = Dict( "residential"=> 6339.0, --> 7692.0 diff --git a/reoptjl/models.py b/reoptjl/models.py index 2f203843e..b128ff11f 100644 --- a/reoptjl/models.py +++ b/reoptjl/models.py @@ -4432,13 +4432,13 @@ class CHPInputs(BaseModel, models.Model): #Financial and emissions macrs_option_years = models.IntegerField( - default=MACRS_YEARS_CHOICES.ZERO, + default=MACRS_YEARS_CHOICES.FIVE, choices=MACRS_YEARS_CHOICES.choices, blank=True, help_text="Duration over which accelerated depreciation will occur. Set to zero to disable" ) macrs_bonus_fraction = models.FloatField( - default=0.0, + default=1.0, validators=[ MinValueValidator(0), MaxValueValidator(1) @@ -6693,7 +6693,7 @@ class SIZE_CLASS_LIST(models.IntegerChoices): ) macrs_option_years = models.IntegerField( - default=MACRS_YEARS_CHOICES.ZERO, + default=MACRS_YEARS_CHOICES.FIVE, choices=MACRS_YEARS_CHOICES.choices, null=True, blank=True, @@ -6701,7 +6701,7 @@ class SIZE_CLASS_LIST(models.IntegerChoices): ) macrs_bonus_fraction = models.FloatField( - default=0.0, + default=1.0, validators=[ MinValueValidator(0), MaxValueValidator(1) From 7d2628487531d65952bb1ce2de356964cc14c0c7 Mon Sep 17 00:00:00 2001 From: Kadlec Date: Fri, 5 Sep 2025 10:34:54 -0600 Subject: [PATCH 46/57] removing CST annual electric consumption from download selected results spreadsheet --- reoptjl/custom_table_config.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/reoptjl/custom_table_config.py b/reoptjl/custom_table_config.py index 0cf41dd05..e99dc0ea8 100644 --- a/reoptjl/custom_table_config.py +++ b/reoptjl/custom_table_config.py @@ -858,12 +858,6 @@ "bau_value" : lambda df: safe_get(df, "outputs.SteamTurbine.annual_electric_production_kwh_bau"), "scenario_value": lambda df: safe_get(df, "outputs.SteamTurbine.annual_electric_production_kwh") }, - { - "label" : "CST Total Electricity Produced (kWh/yr)", - "key" : "cst_total_electricity_produced", - "bau_value" : lambda df: safe_get(df, "outputs.CST.annual_electric_production_kwh_bau"), - "scenario_value": lambda df: safe_get(df, "outputs.CST.annual_electric_production_kwh") - }, ##################################################################################################### ############################## Annual Heating Thermal Production ############################# ##################################################################################################### From aa07560aed1111ddad8a4f971862cf470b7c2ae0 Mon Sep 17 00:00:00 2001 From: adfarth Date: Fri, 5 Sep 2025 11:56:06 -0600 Subject: [PATCH 47/57] Create 0094_alter_chpinputs_macrs_bonus_fraction_and_more.py --- ...chpinputs_macrs_bonus_fraction_and_more.py | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 reoptjl/migrations/0094_alter_chpinputs_macrs_bonus_fraction_and_more.py diff --git a/reoptjl/migrations/0094_alter_chpinputs_macrs_bonus_fraction_and_more.py b/reoptjl/migrations/0094_alter_chpinputs_macrs_bonus_fraction_and_more.py new file mode 100644 index 000000000..2af077603 --- /dev/null +++ b/reoptjl/migrations/0094_alter_chpinputs_macrs_bonus_fraction_and_more.py @@ -0,0 +1,34 @@ +# Generated by Django 4.0.7 on 2025-09-05 17:55 + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('reoptjl', '0093_alter_chpinputs_federal_itc_fraction_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='chpinputs', + name='macrs_bonus_fraction', + field=models.FloatField(blank=True, default=1.0, help_text='Percent of upfront project costs to depreciate in year one in addition to scheduled depreciation', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1)]), + ), + migrations.AlterField( + model_name='chpinputs', + name='macrs_option_years', + field=models.IntegerField(blank=True, choices=[(0, 'Zero'), (5, 'Five'), (7, 'Seven')], default=5, help_text='Duration over which accelerated depreciation will occur. Set to zero to disable'), + ), + migrations.AlterField( + model_name='steamturbineinputs', + name='macrs_bonus_fraction', + field=models.FloatField(blank=True, default=1.0, help_text='Percent of upfront project costs to depreciate in year one in addition to scheduled depreciation', null=True, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1)]), + ), + migrations.AlterField( + model_name='steamturbineinputs', + name='macrs_option_years', + field=models.IntegerField(blank=True, choices=[(0, 'Zero'), (5, 'Five'), (7, 'Seven')], default=5, help_text='Duration over which accelerated depreciation will occur. Set to zero to disable', null=True), + ), + ] From d27b6690295ce57335188913a50cf585ac53e341 Mon Sep 17 00:00:00 2001 From: Kadlec Date: Fri, 5 Sep 2025 15:20:09 -0600 Subject: [PATCH 48/57] modifying high temp thermal storage inputs --- ...einputs_installed_cost_per_kwh_and_more.py | 29 +++++++++++++++++++ reoptjl/models.py | 6 ++-- 2 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 reoptjl/migrations/0101_alter_hightempthermalstorageinputs_installed_cost_per_kwh_and_more.py diff --git a/reoptjl/migrations/0101_alter_hightempthermalstorageinputs_installed_cost_per_kwh_and_more.py b/reoptjl/migrations/0101_alter_hightempthermalstorageinputs_installed_cost_per_kwh_and_more.py new file mode 100644 index 000000000..4fca3fbb7 --- /dev/null +++ b/reoptjl/migrations/0101_alter_hightempthermalstorageinputs_installed_cost_per_kwh_and_more.py @@ -0,0 +1,29 @@ +# Generated by Django 4.0.7 on 2025-09-05 21:17 + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('reoptjl', '0100_hightempthermalstorageinputs_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='hightempthermalstorageinputs', + name='installed_cost_per_kwh', + field=models.FloatField(blank=True, default=62, help_text='Installed hot TES cost in $/kwh', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(10000.0)]), + ), + migrations.AlterField( + model_name='hightempthermalstorageinputs', + name='macrs_bonus_fraction', + field=models.FloatField(blank=True, default=1.0, help_text='Percent of upfront project costs to depreciate in year one in addition to scheduled depreciation', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1)]), + ), + migrations.AlterField( + model_name='hightempthermalstorageinputs', + name='macrs_option_years', + field=models.IntegerField(blank=True, choices=[(0, 'Zero'), (5, 'Five'), (7, 'Seven')], default=5, help_text='Duration over which accelerated depreciation will occur. Set to zero to disable'), + ), + ] diff --git a/reoptjl/models.py b/reoptjl/models.py index b0101aeab..e98b2cfe6 100644 --- a/reoptjl/models.py +++ b/reoptjl/models.py @@ -7091,7 +7091,7 @@ class HighTempThermalStorageInputs(BaseModel, models.Model): help_text="Battery state of charge at first hour of optimization as fraction of energy capacity." ) installed_cost_per_kwh = models.FloatField( - default=1.5, + default=62, validators=[ MinValueValidator(0), MaxValueValidator(1.0e4) @@ -7118,13 +7118,13 @@ class HighTempThermalStorageInputs(BaseModel, models.Model): help_text="Thermal energy-based cost of TES (e.g. volume of the tank)" ) macrs_option_years = models.IntegerField( - default=MACRS_YEARS_CHOICES.SEVEN, + default=MACRS_YEARS_CHOICES.FIVE, choices=MACRS_YEARS_CHOICES.choices, blank=True, help_text="Duration over which accelerated depreciation will occur. Set to zero to disable" ) macrs_bonus_fraction = models.FloatField( - default=0.6, + default=1.0, validators=[ MinValueValidator(0), MaxValueValidator(1) From 1896ae21a9d051643c9a25b4331220f3a7f082dc Mon Sep 17 00:00:00 2001 From: adfarth Date: Fri, 5 Sep 2025 16:40:42 -0600 Subject: [PATCH 49/57] Update Manifest.toml --- julia_src/Manifest.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/julia_src/Manifest.toml b/julia_src/Manifest.toml index fb7eca19a..51fb10779 100644 --- a/julia_src/Manifest.toml +++ b/julia_src/Manifest.toml @@ -922,8 +922,8 @@ uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" [[deps.REopt]] deps = ["ArchGDAL", "CSV", "CoolProp", "DataFrames", "Dates", "DelimitedFiles", "HTTP", "JLD", "JSON", "JuMP", "LinDistFlow", "LinearAlgebra", "Logging", "MathOptInterface", "Requires", "Roots", "Statistics", "TestEnv"] -git-tree-sha1 = "7152b01634998853fac6b0fd386d46d22b83c474" -repo-rev = "misc-defaults" +git-tree-sha1 = "facf03d9eb09c6b03e0ec56f7d22dbedc7e3ca23" +repo-rev = "develop" repo-url = "https://github.com/NREL/REopt.jl.git" uuid = "d36ad4e8-d74a-4f7a-ace1-eaea049febf6" version = "0.53.2" From 291299f49bc11ada03016c97c684b34fd236faab Mon Sep 17 00:00:00 2001 From: bill-becker Date: Mon, 8 Sep 2025 09:36:25 -0600 Subject: [PATCH 50/57] Update default CST and HighTempThermalStorage costs --- ...stinputs_installed_cost_per_kw_and_more.py | 39 +++++++++++++++++++ reoptjl/models.py | 10 ++--- 2 files changed, 44 insertions(+), 5 deletions(-) create mode 100644 reoptjl/migrations/0102_alter_cstinputs_installed_cost_per_kw_and_more.py diff --git a/reoptjl/migrations/0102_alter_cstinputs_installed_cost_per_kw_and_more.py b/reoptjl/migrations/0102_alter_cstinputs_installed_cost_per_kw_and_more.py new file mode 100644 index 000000000..bcf048a0b --- /dev/null +++ b/reoptjl/migrations/0102_alter_cstinputs_installed_cost_per_kw_and_more.py @@ -0,0 +1,39 @@ +# Generated by Django 4.0.7 on 2025-09-07 05:41 + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('reoptjl', '0101_alter_hightempthermalstorageinputs_installed_cost_per_kwh_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='cstinputs', + name='installed_cost_per_kw', + field=models.FloatField(blank=True, default=2200.0, help_text='Installed CST cost in $/kW', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(100000.0)]), + ), + migrations.AlterField( + model_name='cstinputs', + name='macrs_bonus_fraction', + field=models.FloatField(blank=True, default=0.0, help_text='Percent of upfront project costs to depreciate in year one in addition to scheduled depreciation', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1)]), + ), + migrations.AlterField( + model_name='cstinputs', + name='macrs_option_years', + field=models.IntegerField(blank=True, choices=[(0, 'Zero'), (5, 'Five'), (7, 'Seven')], default=0, help_text='Duration over which accelerated depreciation will occur. Set to zero to disable'), + ), + migrations.AlterField( + model_name='cstinputs', + name='om_cost_per_kw', + field=models.FloatField(blank=True, default=33.0, help_text='Annual CST operations and maintenance costs in $/kW', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1000.0)]), + ), + migrations.AlterField( + model_name='hightempthermalstorageinputs', + name='installed_cost_per_kwh', + field=models.FloatField(blank=True, default=86.0, help_text='Installed hot TES cost in $/kwh', validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(10000.0)]), + ), + ] diff --git a/reoptjl/models.py b/reoptjl/models.py index e98b2cfe6..c4699a5f6 100644 --- a/reoptjl/models.py +++ b/reoptjl/models.py @@ -7091,7 +7091,7 @@ class HighTempThermalStorageInputs(BaseModel, models.Model): help_text="Battery state of charge at first hour of optimization as fraction of energy capacity." ) installed_cost_per_kwh = models.FloatField( - default=62, + default=86.0, validators=[ MinValueValidator(0), MaxValueValidator(1.0e4) @@ -8881,7 +8881,7 @@ class CSTInputs(BaseModel, models.Model): help_text="Power density for CST" ) installed_cost_per_kw = models.FloatField( - default=1200, + default=2200.0, validators=[ MinValueValidator(0), MaxValueValidator(1.0e5) @@ -8890,7 +8890,7 @@ class CSTInputs(BaseModel, models.Model): help_text="Installed CST cost in $/kW" ) om_cost_per_kw = models.FloatField( - default=0.0, + default=33.0, validators=[ MinValueValidator(0), MaxValueValidator(1.0e3) @@ -8908,13 +8908,13 @@ class CSTInputs(BaseModel, models.Model): help_text="Annual CST operations and maintenance costs in $/kWh" ) macrs_option_years = models.IntegerField( - default=MACRS_YEARS_CHOICES.FIVE, + default=MACRS_YEARS_CHOICES.ZERO, choices=MACRS_YEARS_CHOICES.choices, blank=True, help_text="Duration over which accelerated depreciation will occur. Set to zero to disable" ) macrs_bonus_fraction = models.FloatField( - default=0.6, + default=0.0, validators=[ MinValueValidator(0), MaxValueValidator(1) From a00adc65f556577efd64d343851c35ca9d88d998 Mon Sep 17 00:00:00 2001 From: bill-becker Date: Mon, 8 Sep 2025 09:40:52 -0600 Subject: [PATCH 51/57] Add REopt email to .env for CST --- julia_src/.env | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/julia_src/.env b/julia_src/.env index 53ef8e1c8..db3264dcd 100644 --- a/julia_src/.env +++ b/julia_src/.env @@ -1 +1,2 @@ -NREL_DEVELOPER_API_KEY="gAfosXcQ9Ldfw3qXqvKVb7PxMEkYigozmC9R3mXQ" \ No newline at end of file +NREL_DEVELOPER_API_KEY="gAfosXcQ9Ldfw3qXqvKVb7PxMEkYigozmC9R3mXQ" +NREL_DEVELOPER_EMAIL="reopt@nrel.gov" \ No newline at end of file From dfcca7c5d8771087865ab86982faf6a6c3b0e68f Mon Sep 17 00:00:00 2001 From: bill-becker Date: Mon, 8 Sep 2025 09:46:21 -0600 Subject: [PATCH 52/57] Update REopt#add-cst-reopt with cost updates --- julia_src/Manifest.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/julia_src/Manifest.toml b/julia_src/Manifest.toml index 1b73dcec4..e9a4125fb 100644 --- a/julia_src/Manifest.toml +++ b/julia_src/Manifest.toml @@ -932,7 +932,7 @@ uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" [[deps.REopt]] deps = ["ArchGDAL", "CSV", "CoolProp", "DataFrames", "Dates", "DelimitedFiles", "HTTP", "JLD", "JSON", "JuMP", "LinDistFlow", "LinearAlgebra", "Logging", "MathOptInterface", "Requires", "Roots", "Statistics", "TestEnv"] -git-tree-sha1 = "ad1484650232c00b97841d2186820ee0b5abd3b0" +git-tree-sha1 = "8641900b19023e27570f7d7bf0fca989fd9e372f" repo-rev = "add-cst-reopt" repo-url = "https://github.com/NREL/REopt.jl.git" uuid = "d36ad4e8-d74a-4f7a-ace1-eaea049febf6" From dfe4c98e12315d5e0aaf48194961970c33af7438 Mon Sep 17 00:00:00 2001 From: bill-becker Date: Fri, 12 Sep 2025 09:29:16 -0600 Subject: [PATCH 53/57] Change restart celery-julia pods to 1AM instead of 1:15PM MT --- Jenkinsfile-restart-celery-julia | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile-restart-celery-julia b/Jenkinsfile-restart-celery-julia index 5665a6cb4..a463d10dc 100644 --- a/Jenkinsfile-restart-celery-julia +++ b/Jenkinsfile-restart-celery-julia @@ -14,7 +14,7 @@ pipeline { NREL_ROOT_CERT_URL_ROOT = credentials("reopt-api-nrel-root-cert-url-root") } triggers { - cron('15 13 * * *') // 1:15 PM MST/MDT + cron('0 1 * * *') // 1:00 AM MST/MDT } parameters { From fb23f3775c8076e74d460baf4e55fc9c6fe1e7ca Mon Sep 17 00:00:00 2001 From: adfarth Date: Tue, 16 Sep 2025 15:26:14 -0600 Subject: [PATCH 54/57] update REopt version --- julia_src/Manifest.toml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/julia_src/Manifest.toml b/julia_src/Manifest.toml index 51fb10779..756844116 100644 --- a/julia_src/Manifest.toml +++ b/julia_src/Manifest.toml @@ -922,11 +922,9 @@ uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" [[deps.REopt]] deps = ["ArchGDAL", "CSV", "CoolProp", "DataFrames", "Dates", "DelimitedFiles", "HTTP", "JLD", "JSON", "JuMP", "LinDistFlow", "LinearAlgebra", "Logging", "MathOptInterface", "Requires", "Roots", "Statistics", "TestEnv"] -git-tree-sha1 = "facf03d9eb09c6b03e0ec56f7d22dbedc7e3ca23" -repo-rev = "develop" -repo-url = "https://github.com/NREL/REopt.jl.git" +git-tree-sha1 = "f9fd5a8419a3a1c057403fb34fd4f47c15afe28e" uuid = "d36ad4e8-d74a-4f7a-ace1-eaea049febf6" -version = "0.53.2" +version = "0.54.0" [[deps.Random]] deps = ["SHA"] From 6c38ced50ac91a159332150699665899216bbea6 Mon Sep 17 00:00:00 2001 From: bill-becker Date: Wed, 17 Sep 2025 15:57:54 -0600 Subject: [PATCH 55/57] Merge migrations after merging misc-defaults --- reoptjl/migrations/0103_merge_20250917_2157.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 reoptjl/migrations/0103_merge_20250917_2157.py diff --git a/reoptjl/migrations/0103_merge_20250917_2157.py b/reoptjl/migrations/0103_merge_20250917_2157.py new file mode 100644 index 000000000..4b7773921 --- /dev/null +++ b/reoptjl/migrations/0103_merge_20250917_2157.py @@ -0,0 +1,14 @@ +# Generated by Django 4.0.7 on 2025-09-17 21:57 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('reoptjl', '0094_alter_chpinputs_macrs_bonus_fraction_and_more'), + ('reoptjl', '0102_alter_cstinputs_installed_cost_per_kw_and_more'), + ] + + operations = [ + ] From b07b323260a3306ccbe954c8659762705224859c Mon Sep 17 00:00:00 2001 From: bill-becker Date: Wed, 24 Sep 2025 08:10:40 -0600 Subject: [PATCH 56/57] Add CST and HighTempThermalStorage additions to CHANGELOG --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 09bc917a5..b076f84d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,8 +26,12 @@ Classify the change according to the following categories: ##### Removed ### Patches -## misc-defaults +## v3.14.0 ### Minor Updates +#### Added +- `CST` (concentrating solar thermal) Intputs and Outputs models; see /help endpoint for model fields +- `HighTempThermalStorage` Inputs and Outputs models; see /help endpoint for model fields + #### Changed Update the following inputs from the previous --> new values: - `Financial.offtaker_discount_rate_fraction`: 0.0638 --> 0.0624 From 010c911d3ace01610b0792e8b1c37b478cf24b53 Mon Sep 17 00:00:00 2001 From: Bill Becker <42586683+Bill-Becker@users.noreply.github.com> Date: Wed, 24 Sep 2025 08:18:25 -0600 Subject: [PATCH 57/57] Remove duplicate model import in reoptjl/validators.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- reoptjl/validators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reoptjl/validators.py b/reoptjl/validators.py index 3429a0291..306814dd8 100644 --- a/reoptjl/validators.py +++ b/reoptjl/validators.py @@ -5,7 +5,7 @@ FinancialInputs, BaseModel, Message, ElectricUtilityInputs, PVInputs, CSTInputs, ElectricStorageInputs, GeneratorInputs, WindInputs, SpaceHeatingLoadInputs, \ DomesticHotWaterLoadInputs, CHPInputs, CoolingLoadInputs, ExistingChillerInputs, HotThermalStorageInputs, ColdThermalStorageInputs, \ AbsorptionChillerInputs, BoilerInputs, SteamTurbineInputs, GHPInputs, ProcessHeatLoadInputs, ElectricHeaterInputs, ASHPSpaceHeaterInputs, \ - ASHPWaterHeaterInputs, CSTInputs, HighTempThermalStorageInputs + ASHPWaterHeaterInputs, HighTempThermalStorageInputs from django.core.exceptions import ValidationError from pyproj import Proj from typing import Tuple