diff --git a/poetry.lock b/poetry.lock index 1c20e7b..db9c73c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.4 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" @@ -890,6 +890,101 @@ files = [ [package.extras] all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] +[[package]] +name = "ijson" +version = "3.4.0" +description = "Iterative JSON parser with standard Python iterator interfaces" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "ijson-3.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e27e50f6dcdee648f704abc5d31b976cd2f90b4642ed447cf03296d138433d09"}, + {file = "ijson-3.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2a753be681ac930740a4af9c93cfb4edc49a167faed48061ea650dc5b0f406f1"}, + {file = "ijson-3.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a07c47aed534e0ec198e6a2d4360b259d32ac654af59c015afc517ad7973b7fb"}, + {file = "ijson-3.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c55f48181e11c597cd7146fb31edc8058391201ead69f8f40d2ecbb0b3e4fc6"}, + {file = "ijson-3.4.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd5669f96f79d8a2dd5ae81cbd06770a4d42c435fd4a75c74ef28d9913b697d"}, + {file = "ijson-3.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e3ddd46d16b8542c63b1b8af7006c758d4e21cc1b86122c15f8530fae773461"}, + {file = "ijson-3.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:1504cec7fe04be2bb0cc33b50c9dd3f83f98c0540ad4991d4017373b7853cfe6"}, + {file = "ijson-3.4.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:2f2ff456adeb216603e25d7915f10584c1b958b6eafa60038d76d08fc8a5fb06"}, + {file = "ijson-3.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0ab00d75d61613a125fbbb524551658b1ad6919a52271ca16563ca5bc2737bb1"}, + {file = "ijson-3.4.0-cp310-cp310-win32.whl", hash = "sha256:ada421fd59fe2bfa4cfa64ba39aeba3f0753696cdcd4d50396a85f38b1d12b01"}, + {file = "ijson-3.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:8c75e82cec05d00ed3a4af5f4edf08f59d536ed1a86ac7e84044870872d82a33"}, + {file = "ijson-3.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9e369bf5a173ca51846c243002ad8025d32032532523b06510881ecc8723ee54"}, + {file = "ijson-3.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:26e7da0a3cd2a56a1fde1b34231867693f21c528b683856f6691e95f9f39caec"}, + {file = "ijson-3.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1c28c7f604729be22aa453e604e9617b665fa0c24cd25f9f47a970e8130c571a"}, + {file = "ijson-3.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bed8bcb84d3468940f97869da323ba09ae3e6b950df11dea9b62e2b231ca1e3"}, + {file = "ijson-3.4.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:296bc824f4088f2af814aaf973b0435bc887ce3d9f517b1577cc4e7d1afb1cb7"}, + {file = "ijson-3.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8145f8f40617b6a8aa24e28559d0adc8b889e56a203725226a8a60fa3501073f"}, + {file = "ijson-3.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b674a97bd503ea21bc85103e06b6493b1b2a12da3372950f53e1c664566a33a4"}, + {file = "ijson-3.4.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8bc731cf1c3282b021d3407a601a5a327613da9ad3c4cecb1123232623ae1826"}, + {file = "ijson-3.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:42ace5e940e0cf58c9de72f688d6829ddd815096d07927ee7e77df2648006365"}, + {file = "ijson-3.4.0-cp311-cp311-win32.whl", hash = "sha256:5be39a0df4cd3f02b304382ea8885391900ac62e95888af47525a287c50005e9"}, + {file = "ijson-3.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:0b1be1781792291e70d2e177acf564ec672a7907ba74f313583bdf39fe81f9b7"}, + {file = "ijson-3.4.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:956b148f88259a80a9027ffbe2d91705fae0c004fbfba3e5a24028fbe72311a9"}, + {file = "ijson-3.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:06b89960f5c721106394c7fba5760b3f67c515b8eb7d80f612388f5eca2f4621"}, + {file = "ijson-3.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9a0bb591cf250dd7e9dfab69d634745a7f3272d31cfe879f9156e0a081fd97ee"}, + {file = "ijson-3.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:72e92de999977f4c6b660ffcf2b8d59604ccd531edcbfde05b642baf283e0de8"}, + {file = "ijson-3.4.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e9602157a5b869d44b6896e64f502c712a312fcde044c2e586fccb85d3e316e"}, + {file = "ijson-3.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1e83660edb931a425b7ff662eb49db1f10d30ca6d4d350e5630edbed098bc01"}, + {file = "ijson-3.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:49bf8eac1c7b7913073865a859c215488461f7591b4fa6a33c14b51cb73659d0"}, + {file = "ijson-3.4.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:160b09273cb42019f1811469508b0a057d19f26434d44752bde6f281da6d3f32"}, + {file = "ijson-3.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2019ff4e6f354aa00c76c8591bd450899111c61f2354ad55cc127e2ce2492c44"}, + {file = "ijson-3.4.0-cp312-cp312-win32.whl", hash = "sha256:931c007bf6bb8330705429989b2deed6838c22b63358a330bf362b6e458ba0bf"}, + {file = "ijson-3.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:71523f2b64cb856a820223e94d23e88369f193017ecc789bb4de198cc9d349eb"}, + {file = "ijson-3.4.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e8d96f88d75196a61c9d9443de2b72c2d4a7ba9456ff117b57ae3bba23a54256"}, + {file = "ijson-3.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c45906ce2c1d3b62f15645476fc3a6ca279549127f01662a39ca5ed334a00cf9"}, + {file = "ijson-3.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4ab4bc2119b35c4363ea49f29563612237cae9413d2fbe54b223be098b97bc9e"}, + {file = "ijson-3.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97b0a9b5a15e61dfb1f14921ea4e0dba39f3a650df6d8f444ddbc2b19b479ff1"}, + {file = "ijson-3.4.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e3047bb994dabedf11de11076ed1147a307924b6e5e2df6784fb2599c4ad8c60"}, + {file = "ijson-3.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68c83161b052e9f5dc8191acbc862bb1e63f8a35344cb5cd0db1afd3afd487a6"}, + {file = "ijson-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1eebd9b6c20eb1dffde0ae1f0fbb4aeacec2eb7b89adb5c7c0449fc9fd742760"}, + {file = "ijson-3.4.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:13fb6d5c35192c541421f3ee81239d91fc15a8d8f26c869250f941f4b346a86c"}, + {file = "ijson-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:28b7196ff7b37c4897c547a28fa4876919696739fc91c1f347651c9736877c69"}, + {file = "ijson-3.4.0-cp313-cp313-win32.whl", hash = "sha256:3c2691d2da42629522140f77b99587d6f5010440d58d36616f33bc7bdc830cc3"}, + {file = "ijson-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:c4554718c275a044c47eb3874f78f2c939f300215d9031e785a6711cc51b83fc"}, + {file = "ijson-3.4.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:915a65e3f3c0eee2ea937bc62aaedb6c14cc1e8f0bb9f3f4fb5a9e2bbfa4b480"}, + {file = "ijson-3.4.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:afbe9748707684b6c5adc295c4fdcf27765b300aec4d484e14a13dca4e5c0afa"}, + {file = "ijson-3.4.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d823f8f321b4d8d5fa020d0a84f089fec5d52b7c0762430476d9f8bf95bbc1a9"}, + {file = "ijson-3.4.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b8a0a2c54f3becf76881188beefd98b484b1d3bd005769a740d5b433b089fa23"}, + {file = "ijson-3.4.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ced19a83ab09afa16257a0b15bc1aa888dbc555cb754be09d375c7f8d41051f2"}, + {file = "ijson-3.4.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8100f9885eff1f38d35cef80ef759a1bbf5fc946349afa681bd7d0e681b7f1a0"}, + {file = "ijson-3.4.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d7bcc3f7f21b0f703031ecd15209b1284ea51b2a329d66074b5261de3916c1eb"}, + {file = "ijson-3.4.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2dcb190227b09dd171bdcbfe4720fddd574933c66314818dfb3960c8a6246a77"}, + {file = "ijson-3.4.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:eda4cfb1d49c6073a901735aaa62e39cb7ab47f3ad7bb184862562f776f1fa8a"}, + {file = "ijson-3.4.0-cp313-cp313t-win32.whl", hash = "sha256:0772638efa1f3b72b51736833404f1cbd2f5beeb9c1a3d392e7d385b9160cba7"}, + {file = "ijson-3.4.0-cp313-cp313t-win_amd64.whl", hash = "sha256:3d8a0d67f36e4fb97c61a724456ef0791504b16ce6f74917a31c2e92309bbeb9"}, + {file = "ijson-3.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8a990401dc7350c1739f42187823e68d2ef6964b55040c6e9f3a29461f9929e2"}, + {file = "ijson-3.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:80f50e0f5da4cd6b65e2d8ff38cb61b26559608a05dd3a3f9cfa6f19848e6f22"}, + {file = "ijson-3.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2d9ca52f5650d820a2e7aa672dea1c560f609e165337e5b3ed7cf56d696bf309"}, + {file = "ijson-3.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:940c8c5fd20fb89b56dde9194a4f1c7b779149f1ab26af6d8dc1da51a95d26dd"}, + {file = "ijson-3.4.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:41dbb525666017ad856ac9b4f0f4b87d3e56b7dfde680d5f6d123556b22e2172"}, + {file = "ijson-3.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9f84f5e2eea5c2d271c97221c382db005534294d1175ddd046a12369617c41c"}, + {file = "ijson-3.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:c0cd126c11835839bba8ac0baaba568f67d701fc4f717791cf37b10b74a2ebd7"}, + {file = "ijson-3.4.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f9a9d3bbc6d91c24a2524a189d2aca703cb5f7e8eb34ad0aff3c91702404a983"}, + {file = "ijson-3.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:56679ee133470d0f1f598a8ad109d760fcfebeef4819531e29335aefb7e4cb1a"}, + {file = "ijson-3.4.0-cp39-cp39-win32.whl", hash = "sha256:583c15ded42ba80104fa1d0fa0dfdd89bb47922f3bb893a931bb843aeb55a3f3"}, + {file = "ijson-3.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:4563e603e56f4451572d96b47311dffef5b933d825f3417881d4d3630c6edac2"}, + {file = "ijson-3.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:54e989c35dba9cf163d532c14bcf0c260897d5f465643f0cd1fba9c908bed7ef"}, + {file = "ijson-3.4.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:494eeb8e87afef22fbb969a4cb81ac2c535f30406f334fb6136e9117b0bb5380"}, + {file = "ijson-3.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81603de95de1688958af65cd2294881a4790edae7de540b70c65c8253c5dc44a"}, + {file = "ijson-3.4.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8524be12c1773e1be466034cc49c1ecbe3d5b47bb86217bd2a57f73f970a6c19"}, + {file = "ijson-3.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17994696ec895d05e0cfa21b11c68c920c82634b4a3d8b8a1455d6fe9fdee8f7"}, + {file = "ijson-3.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0b67727aaee55d43b2e82b6a866c3cbcb2b66a5e9894212190cbd8773d0d9857"}, + {file = "ijson-3.4.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:cdc8c5ca0eec789ed99db29c68012dda05027af0860bb360afd28d825238d69d"}, + {file = "ijson-3.4.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:8e6b44b6ec45d5b1a0ee9d97e0e65ab7f62258727004cbbe202bf5f198bc21f7"}, + {file = "ijson-3.4.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b51e239e4cb537929796e840d349fc731fdc0d58b1a0683ce5465ad725321e0f"}, + {file = "ijson-3.4.0-pp311-pypy311_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed05d43ec02be8ddb1ab59579761f6656b25d241a77fd74f4f0f7ec09074318a"}, + {file = "ijson-3.4.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cfeca1aaa59d93fd0a3718cbe5f7ef0effff85cf837e0bceb71831a47f39cc14"}, + {file = "ijson-3.4.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:7ca72ca12e9a1dd4252c97d952be34282907f263f7e28fcdff3a01b83981e837"}, + {file = "ijson-3.4.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0f79b2cd52bd220fff83b3ee4ef89b54fd897f57cc8564a6d8ab7ac669de3930"}, + {file = "ijson-3.4.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:d16eed737610ad5ad8989b5864fbe09c64133129734e840c29085bb0d497fb03"}, + {file = "ijson-3.4.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b3aac1d7a27e1e3bdec5bd0689afe55c34aa499baa06a80852eda31f1ffa6dc"}, + {file = "ijson-3.4.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:784ae654aa9851851e87f323e9429b20b58a5399f83e6a7e348e080f2892081f"}, + {file = "ijson-3.4.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d05bd8fa6a8adefb32bbf7b993d2a2f4507db08453dd1a444c281413a6d9685"}, + {file = "ijson-3.4.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:b5a05fd935cc28786b88c16976313086cd96414c6a3eb0a3822c47ab48b1793e"}, + {file = "ijson-3.4.0.tar.gz", hash = "sha256:5f74dcbad9d592c428d3ca3957f7115a42689ee7ee941458860900236ae9bb13"}, +] + [[package]] name = "importlib-resources" version = "6.4.5" @@ -2682,4 +2777,4 @@ propcache = ">=0.2.1" [metadata] lock-version = "2.1" python-versions = "^3.11" -content-hash = "9515262811b7bbd860f9645b0b97f74abc4253ed33edd9c2eb843a056ad757f2" +content-hash = "c77b9ab7225ed5fd08d9945ae8f411394ffdc5be23414a79801c3c450e6ff8df" diff --git a/pyproject.toml b/pyproject.toml index 0209f4b..65a0e8a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,6 +29,7 @@ greenlet = "^3.1.1" isort = "^6.0.1" dotenv = "^0.9.9" aiohttp = "^3.12.14" +ijson = "^3.4.0" [build-system] diff --git a/src/aeros_equipment/service.py b/src/aeros_equipment/service.py index 2bfa9eb..ac3d400 100644 --- a/src/aeros_equipment/service.py +++ b/src/aeros_equipment/service.py @@ -265,7 +265,7 @@ def get_asset_batch(location_tags: List[str], nr_location_tags: List[str], mttr = item["mttr"] distribution, reldisp1, reldisp2 = get_distribution(item) - results[location_tag]["cmDisP1"] = mttr + results[location_tag]["cmDisP1"] = 0.5 if mttr > 0 else 0 results[location_tag]["relDisType"] = distribution results[location_tag]["relDisP1"] = reldisp1 results[location_tag]["relDisP2"] = reldisp2 diff --git a/src/aeros_simulation/service.py b/src/aeros_simulation/service.py index eb20fcf..40d8aaa 100644 --- a/src/aeros_simulation/service.py +++ b/src/aeros_simulation/service.py @@ -1,9 +1,11 @@ from datetime import datetime +import json from typing import Optional from uuid import uuid4, uuid4, UUID import logging import httpx from fastapi import HTTPException, status +import ijson from sqlalchemy import delete, select, update, and_ from sqlalchemy.orm import selectinload @@ -31,6 +33,7 @@ client = httpx.AsyncClient(timeout=300.0) active_simulations = {} +# Get Data Service async def get_all(common: CommonParameters): query = select(AerosSimulation).where(AerosSimulation.status == "completed") @@ -38,6 +41,19 @@ async def get_all(common: CommonParameters): return results +async def get_all_aeros_node(*, db_session: DbSession, schematic_name: Optional[str] = None): + query = select(AerosNode) + + if schematic_name: + aeros_schematic = await get_aeros_schematic_by_name(db_session=db_session, schematic_name=schematic_name) + + if not aeros_schematic: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Schematic not found") + + query = query.where(AerosNode.aeros_schematic_id == aeros_schematic.id) + + results = await db_session.execute(query) + return results.scalars().all() async def get_simulation_by_id( *, @@ -59,7 +75,6 @@ async def get_simulation_by_id( results = await db_session.execute(query) return results.scalar() - async def get_simulation_node_by(*, db_session: DbSession, **kwargs): """Get a simulation node by column.""" # Build WHERE conditions from kwargs @@ -75,15 +90,12 @@ async def get_simulation_node_by(*, db_session: DbSession, **kwargs): result = await db_session.execute(query) return result.scalar() - async def get_or_save_node(*, db_session: DbSession, node_data: dict, type: str = "calc"): """Get a simulation node by column.""" node = await get_simulation_node_by( db_session=db_session, node_name=node_data["nodeName"] ) - raise Exception(node_data) - if not node: print("Creating new node") @@ -131,6 +143,195 @@ async def get_or_save_node(*, db_session: DbSession, node_data: dict, type: str return node +async def get_aeros_schematic_by_name(*, db_session: DbSession, schematic_name: str): + query = select(AerosSchematic).where(AerosSchematic.schematic_name == schematic_name) + results = await db_session.execute(query) + return results.scalar_one_or_none() + +async def get_simulation_with_calc_result( + *, db_session: DbSession, simulation_id: UUID, aeros_node_id: Optional[UUID] = None, schematic_name: Optional[str] = None, node_type: Optional[str] = None +): + """Get a simulation by id.""" + query = (select(AerosSimulationCalcResult).filter( + AerosSimulationCalcResult.aeros_simulation_id == simulation_id)) + + if schematic_name: + if schematic_name == "WTP": + query = query.join( + AerosNode, AerosNode.id == AerosSimulationCalcResult.aeros_node_id + ).filter(AerosNode.structure_name.contains(schematic_name)) + else: + query = query.join( + AerosNode, AerosNode.id == AerosSimulationCalcResult.aeros_node_id + ).filter(AerosNode.structure_name.contains(schematic_name)) + + if node_type: + query = query.join( + AerosNode, AerosNode.id == AerosSimulationCalcResult.aeros_node_id + ).filter(AerosNode.node_type == node_type) + + query = query.options( + selectinload(AerosSimulationCalcResult.aeros_node).options( + selectinload(AerosNode.equipment) + )) + + simulation = await db_session.execute(query) + + return simulation.scalars().all() + +async def get_plant_calc_result( + *, db_session, simulation_id: UUID +): + query = (select(AerosSimulationCalcResult).filter( + AerosSimulationCalcResult.aeros_simulation_id == simulation_id, + ).join(AerosNode, AerosNode.id == AerosSimulationCalcResult.aeros_node_id) + .filter(AerosNode.node_name == "- TJB - Unit 3 -")) + + query = query.options( + selectinload(AerosSimulationCalcResult.aeros_node).options( + selectinload(AerosNode.equipment) + )) + + calc = await db_session.execute(query) + + return calc.scalar_one_or_none() + +async def get_result_ranking(*, db_session: DbSession, simulation_id: UUID): + + query = select(AerosEquipment, AerosSimulationCalcResult.eaf).join(AerosNode, AerosNode.node_name == AerosEquipment.node_name).join(AerosSimulationCalcResult, AerosSimulationCalcResult.aeros_node_id == AerosNode.id) + + query = query.filter( + and_( + AerosSimulationCalcResult.aeros_simulation_id == simulation_id, + AerosNode.node_type == "RegularNode", + AerosEquipment.custom_parameters.any() + ) + ) + + query = query.order_by(AerosSimulationCalcResult.eaf.desc()) + + + query = query.options( + selectinload(AerosEquipment.custom_parameters)).options( + selectinload(AerosEquipment.master_equipment) + ) + + result = await db_session.execute(query) + + data = [ + SimulationRankingParameters( + location_tag=equipment.location_tag, + master_equipment=equipment.master_equipment, + custom_parameters=equipment.custom_parameters, + eaf=eaf + ) + for equipment, eaf in result + ] + + return data + + +async def get_simulation_with_plot_result( + *, db_session: DbSession, simulation_id: UUID, node_type: Optional[str] = None +): + """Get a simulation by id.""" + query = ( + select(AerosSimulation) + .where(AerosSimulation.id == simulation_id) + .options( + selectinload(AerosSimulation.plot_results).options( + selectinload(AerosSimulationPlotResult.aeros_node) + ) + ) + ) + + if node_type: + query = query.join( + AerosNode, AerosNode.id == AerosSimulation.plot_results.aeros_node_id + ).filter(AerosNode.node_type == node_type) + + simulation = await db_session.execute(query) + return simulation.scalar() + + +async def get_calc_result_by( + *, db_session: DbSession, simulation_id: UUID, node_name: Optional[str] = None +): + """Get a simulation node by column.""" + # Build WHERE conditions from kwargs + query = select(AerosSimulationCalcResult).where( + AerosSimulationCalcResult.aeros_simulation_id == simulation_id + ) + + if node_name: + query = query.join(AerosSimulationCalcResult.aeros_node).filter(AerosNode.node_name == node_name) + + result = await db_session.execute(query) + return result.scalar() + + +async def get_custom_parameters(*, db_session: DbSession, simulation_id: UUID): + """Get a simulation node by column.""" + # Build WHERE conditions from kwargs + query = select(AerosSimulationCalcResult).where( + AerosSimulationCalcResult.aeros_simulation_id == simulation_id + ) + query = query.join( + AerosNode, AerosNode.id == AerosSimulationCalcResult.aeros_node_id + ) + query = query.where(AerosNode.node_type == "RegularNode") + query = ( + query.order_by(AerosSimulationCalcResult.eaf.desc()) + .limit(20) + .options(selectinload(AerosSimulationCalcResult.aeros_node)) + ) + result = await db_session.execute(query) + return result.scalars().all() + +async def get_regular_nodes_by_schematic(*, db_session: DbSession, schematic_name: str) -> set[UUID]: + """ + Get all regular node IDs that are descendants of a given schematic (system or subsystem). + Uses recursive CTE to traverse the hierarchy. + """ + + # Using recursive CTE to find all descendants + # First, find the root node(s) with the given schematic name + root_cte = ( + select(AerosNode.id, AerosNode.schematic_id, AerosNode.ref_schematic_id,AerosNode.node_type, AerosNode.node_name) + .where(AerosNode.node_name == schematic_name) + .cte(name="hierarchy", recursive=True) + ) + + # Recursive part: find all children + children_cte = ( + select(AerosNode.id, AerosNode.schematic_id,AerosNode.ref_schematic_id ,AerosNode.node_type, AerosNode.node_name) + .select_from( + AerosNode.join(root_cte, AerosNode.schematic_id == root_cte.c.ref_schematic_id) + ) + ) + + # Union the base case and recursive case + hierarchy_cte = root_cte.union_all(children_cte) + + # Final query to get only regular nodes from the hierarchy + query = ( + select(hierarchy_cte.c.id) + .where(hierarchy_cte.c.node_type == "RegularNode") # Adjust this condition based on your node_type values + ) + + result = await db_session.execute(query) + return set(result.scalars().all()) + + +async def get_all_schematic_aeros(*, db_session: DbSession): + query = select(AerosSchematic) + results = await db_session.execute(query) + return results.scalars().all() + + + + +# Aeros Simulation Execution Service async def execute_simulation(*, db_session: DbSession, simulation_id: Optional[UUID] = None, sim_data: dict, is_saved: bool = False, eq_update: dict = None): """Execute the actual simulation call""" @@ -139,6 +340,27 @@ async def execute_simulation(*, db_session: DbSession, simulation_id: Optional[U print("Executing simulation with id: %s", simulation_id, sim_data["SchematicName"]) try: + if not is_saved: + response = await client.post( + f"{AEROS_BASE_URL}/api/Simulation/RunSimulation", + json=sim_data, + headers={"Content-Type": "application/json"}, + ) + response.raise_for_status() + result = response.json() + + return result + + simulation = await get_simulation_by_id( + db_session=db_session, simulation_id=simulation_id + ) + + + simulation.status = "processing" + await db_session.commit() + + print("Simulation started with id: %s", simulation.id) + response = await client.post( f"{AEROS_BASE_URL}/api/Simulation/RunSimulation", json=sim_data, @@ -147,19 +369,14 @@ async def execute_simulation(*, db_session: DbSession, simulation_id: Optional[U response.raise_for_status() result = response.json() - if is_saved: - simulation = await get_simulation_by_id( - db_session=db_session, simulation_id=simulation_id - ) - simulation.status = "proccessing" - simulation.result = result - await db_session.commit() - await save_simulation_result( - db_session=db_session, simulation_id=simulation_id, result=result, schematic_name=sim_data["SchematicName"],eq_update=eq_update - ) - - print("Simulation completed with id: %s", simulation_id) - return result + await save_simulation_result( + db_session=db_session, simulation_id=simulation.id, result=result, schematic_name=sim_data["SchematicName"], eq_update=eq_update + ) + print("Simulation completed with id: %s", simulation.id) + simulation.status = "completed" + simulation.completed_at = datetime.now() + await db_session.commit() + return True except Exception as e: simulation = await get_simulation_by_id( @@ -174,22 +391,146 @@ async def execute_simulation(*, db_session: DbSession, simulation_id: Optional[U raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) ) from e + +async def save_simulation_result_streaming( + *, db_session: DbSession, simulation, response_stream, schematic_name: str, eq_update: dict +): + """Save simulation result by streaming and processing in batches.""" + print("Saving simulation result (streaming)") + + # Pre-load available nodes once + available_nodes = { + f"{node.node_type}:{node.node_name}": node + for node in await get_all_aeros_node(db_session=db_session, schematic_name=schematic_name) + } + + batch_size = 100 # Adjust based on your needs + + try: + # Process calc results in batches + await process_calc_results_streaming( + db_session, simulation.id, response_stream, available_nodes, eq_update, batch_size + ) + + # # Process plot results in batches + # await process_plot_results_streaming( + # db_session, simulation_id, response_stream, available_nodes, batch_size + # ) + + # Update simulation status + simulation.status = "completed" + simulation.completed_at = datetime.now() + await db_session.commit() + + except Exception as e: + simulation.status = "failed" + simulation.error = str(e) + await db_session.commit() -async def get_all_aeros_node(*, db_session: DbSession, schematic_name: Optional[str] = None): - query = select(AerosNode) - - if schematic_name: - aeros_schematic = await get_aeros_schematic_by_name(db_session=db_session, schematic_name=schematic_name) - - if not aeros_schematic: - raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Schematic not found") + log.error("Simulation failed with error: %s", str(e)) - query = query.where(AerosNode.aeros_schematic_id == aeros_schematic.id) + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) + ) from e - results = await db_session.execute(query) - return results.scalars().all() +async def process_calc_results_streaming( + db_session, simulation_id, response_stream, available_nodes, eq_update, batch_size +): + """Process calc results in streaming batches.""" + calc_batch = [] + + # Parse nodeResultOuts array incrementally + calc_results = ijson.items(response_stream, 'nodeResultOuts.item') + + raise Exception(calc_results) + + async for result in calc_results: + calc_obj = await create_calc_result_object( + simulation_id, result, available_nodes, eq_update, db_session + ) + if calc_obj: + calc_batch.append(calc_obj) + + # Save batch when it reaches batch_size + if len(calc_batch) >= batch_size: + db_session.add_all(calc_batch) + await db_session.commit() + calc_batch.clear() + + # Save remaining items + if calc_batch: + db_session.add_all(calc_batch) + await db_session.commit() +async def create_calc_result_object( + simulation_id, result, available_nodes, eq_update, db_session +) : + """Create a single calc result object.""" + node_type = "RegularNode" if result["nodeType"] == "RegularNode" else "SchematicNode" + node = available_nodes.get(f"{node_type}:{result['nodeName']}") + + if not node: + if result["nodeType"] not in ["RegularNode", "Schematic"]: + return None + node = await get_or_save_node(db_session=db_session, node_data=result, type="calc") + # Add to available_nodes for future use + available_nodes[f"{node_type}:{result['nodeName']}"] = node + + eq_reliability = eq_update.get(result["nodeName"], { + "eta": 0, "beta": 0, "mttr": 0, "parameters": {} + }) + + eaf, derating_hours = calculate_eaf( + available_hours=result["totalUpTime"], + period_hours=result["totalUpTime"] + result["totalDowntime"], + actual_production=result["production"], + ideal_production=result["idealProduction"] + ) + + efor = (result["totalDowntime"] / (result["totalDowntime"] + result["totalUpTime"])) * 100 if (result["totalDowntime"] + result["totalUpTime"]) > 0 else 0 + + return AerosSimulationCalcResult( + aeros_simulation_id=simulation_id, + aeros_node_id=node.id, + total_downtime=result["totalDowntime"], + total_uptime=result["totalUpTime"], + num_events=result["numEvents"], + production=result["production"], + production_std=result["productionStd"], + ideal_production=result["idealProduction"], + availability=result["availability"], + efficiency=result["efficiency"], + effective_loss=result["effectiveLoss"], + num_cm=result["numCM"], + cm_waiting_time=result["cmWaitingTime"], + total_cm_downtime=result["totalCMDowntime"], + num_pm=result["numPM"], + total_pm_downtime=result["totalPMDowntime"], + num_ip=result["numIP"], + total_ip_downtime=result["totalIPDowntime"], + num_oh=result["numOH"], + total_oh_downtime=result["totalOHDowntime"], + t_wait_for_crew=result["tWaitForCrew"], + t_wait_for_spare=result["tWaitForSpare"], + duration_at_full=result["durationAtFull"], + duration_above_hh=result["durationAboveHH"], + duration_above_h=result["durationAboveH"], + duration_below_l=result["durationBelowL"], + duration_below_ll=result["durationBelowLL"], + duration_at_empty=result["durationAtEmpty"], + stg_input=result["stgInput"], + stg_output=result["stgOutput"], + average_level=result["averageLevel"], + potential_production=result["potentialProduction"], + eaf=eaf, + efor=efor, + derating_hours=derating_hours, + beta=eq_reliability["beta"] if node_type == "RegularNode" else None, + eta=eq_reliability["eta"] if node_type == "RegularNode" else None, + mttr=eq_reliability["mttr"] if node_type == "RegularNode" else None, + parameters=eq_reliability["parameters"] if node_type == "RegularNode" else None + ) async def save_simulation_result( @@ -418,10 +759,6 @@ async def save_recusive_simulation_result_node(*, db_session: DbSession, data, s return results -async def get_aeros_schematic_by_name(*, db_session: DbSession, schematic_name: str): - query = select(AerosSchematic).where(AerosSchematic.schematic_name == schematic_name) - results = await db_session.execute(query) - return results.scalar_one_or_none() async def save_default_simulation_node( *, db_session: DbSession, project_name: str = "trialapi" @@ -489,186 +826,7 @@ async def create_simulation(*, db_session: DbSession, simulation_in: SimulationI return simulation -async def get_simulation_with_calc_result( - *, db_session: DbSession, simulation_id: UUID, aeros_node_id: Optional[UUID] = None, schematic_name: Optional[str] = None, node_type: Optional[str] = None -): - """Get a simulation by id.""" - query = (select(AerosSimulationCalcResult).filter( - AerosSimulationCalcResult.aeros_simulation_id == simulation_id)) - - if schematic_name: - if schematic_name == "WTP": - query = query.join( - AerosNode, AerosNode.id == AerosSimulationCalcResult.aeros_node_id - ).filter(AerosNode.structure_name.contains(schematic_name)) - else: - query = query.join( - AerosNode, AerosNode.id == AerosSimulationCalcResult.aeros_node_id - ).filter(AerosNode.structure_name.contains(schematic_name)) - - if node_type: - query = query.join( - AerosNode, AerosNode.id == AerosSimulationCalcResult.aeros_node_id - ).filter(AerosNode.node_type == node_type) - - query = query.options( - selectinload(AerosSimulationCalcResult.aeros_node).options( - selectinload(AerosNode.equipment) - )) - - simulation = await db_session.execute(query) - - return simulation.scalars().all() - - -async def get_plant_calc_result( - *, db_session, simulation_id: UUID -): - query = (select(AerosSimulationCalcResult).filter( - AerosSimulationCalcResult.aeros_simulation_id == simulation_id, - ).join(AerosNode, AerosNode.id == AerosSimulationCalcResult.aeros_node_id) - .filter(AerosNode.node_name == "- TJB - Unit 3 -")) - - query = query.options( - selectinload(AerosSimulationCalcResult.aeros_node).options( - selectinload(AerosNode.equipment) - )) - - calc = await db_session.execute(query) - - return calc.scalar_one_or_none() - -async def get_result_ranking(*, db_session: DbSession, simulation_id: UUID): - - query = select(AerosEquipment, AerosSimulationCalcResult.eaf).join(AerosNode, AerosNode.node_name == AerosEquipment.node_name).join(AerosSimulationCalcResult, AerosSimulationCalcResult.aeros_node_id == AerosNode.id) - - query = query.filter( - and_( - AerosSimulationCalcResult.aeros_simulation_id == simulation_id, - AerosNode.node_type == "RegularNode", - AerosEquipment.custom_parameters.any() - ) - ) - - query = query.order_by(AerosSimulationCalcResult.eaf.desc()) - - - query = query.options( - selectinload(AerosEquipment.custom_parameters)).options( - selectinload(AerosEquipment.master_equipment) - ) - - result = await db_session.execute(query) - - data = [ - SimulationRankingParameters( - location_tag=equipment.location_tag, - master_equipment=equipment.master_equipment, - custom_parameters=equipment.custom_parameters, - eaf=eaf - ) - for equipment, eaf in result - ] - - return data - - -async def get_simulation_with_plot_result( - *, db_session: DbSession, simulation_id: UUID, node_type: Optional[str] = None -): - """Get a simulation by id.""" - query = ( - select(AerosSimulation) - .where(AerosSimulation.id == simulation_id) - .options( - selectinload(AerosSimulation.plot_results).options( - selectinload(AerosSimulationPlotResult.aeros_node) - ) - ) - ) - if node_type: - query = query.join( - AerosNode, AerosNode.id == AerosSimulation.plot_results.aeros_node_id - ).filter(AerosNode.node_type == node_type) - - simulation = await db_session.execute(query) - return simulation.scalar() - - -async def get_calc_result_by( - *, db_session: DbSession, simulation_id: UUID, node_name: Optional[str] = None -): - """Get a simulation node by column.""" - # Build WHERE conditions from kwargs - query = select(AerosSimulationCalcResult).where( - AerosSimulationCalcResult.aeros_simulation_id == simulation_id - ) - - if node_name: - query = query.join(AerosSimulationCalcResult.aeros_node).filter(AerosNode.node_name == node_name) - - result = await db_session.execute(query) - return result.scalar() - - -async def get_custom_parameters(*, db_session: DbSession, simulation_id: UUID): - """Get a simulation node by column.""" - # Build WHERE conditions from kwargs - query = select(AerosSimulationCalcResult).where( - AerosSimulationCalcResult.aeros_simulation_id == simulation_id - ) - query = query.join( - AerosNode, AerosNode.id == AerosSimulationCalcResult.aeros_node_id - ) - query = query.where(AerosNode.node_type == "RegularNode") - query = ( - query.order_by(AerosSimulationCalcResult.eaf.desc()) - .limit(20) - .options(selectinload(AerosSimulationCalcResult.aeros_node)) - ) - result = await db_session.execute(query) - return result.scalars().all() - -async def get_regular_nodes_by_schematic(*, db_session: DbSession, schematic_name: str) -> set[UUID]: - """ - Get all regular node IDs that are descendants of a given schematic (system or subsystem). - Uses recursive CTE to traverse the hierarchy. - """ - - # Using recursive CTE to find all descendants - # First, find the root node(s) with the given schematic name - root_cte = ( - select(AerosNode.id, AerosNode.schematic_id, AerosNode.ref_schematic_id,AerosNode.node_type, AerosNode.node_name) - .where(AerosNode.node_name == schematic_name) - .cte(name="hierarchy", recursive=True) - ) - - # Recursive part: find all children - children_cte = ( - select(AerosNode.id, AerosNode.schematic_id,AerosNode.ref_schematic_id ,AerosNode.node_type, AerosNode.node_name) - .select_from( - AerosNode.join(root_cte, AerosNode.schematic_id == root_cte.c.ref_schematic_id) - ) - ) - - # Union the base case and recursive case - hierarchy_cte = root_cte.union_all(children_cte) - - # Final query to get only regular nodes from the hierarchy - query = ( - select(hierarchy_cte.c.id) - .where(hierarchy_cte.c.node_type == "RegularNode") # Adjust this condition based on your node_type values - ) - - result = await db_session.execute(query) - return set(result.scalars().all()) - - -async def get_all_schematic_aeros(*, db_session: DbSession): - query = select(AerosSchematic) - results = await db_session.execute(query) - return results.scalars().all() async def update_simulation(*, db_session: DbSession, simulation_id: UUID, data: dict): diff --git a/src/aeros_simulation/utils.py b/src/aeros_simulation/utils.py index f7c0a91..cacf278 100644 --- a/src/aeros_simulation/utils.py +++ b/src/aeros_simulation/utils.py @@ -22,15 +22,12 @@ def calculate_eaf( try: # Calculate lost production lost_production = ideal_production - actual_production - print(ideal_production, actual_production, lost_production) - - estimated_max_capacity = ideal_production / period_hours if period_hours > 0 else 0 - + max_capacity = 660 # Calculate total equivalent derate hours - total_equivalent_derate_hours = lost_production / estimated_max_capacity if estimated_max_capacity > 0 else 0 + total_equivalent_derate_hours = lost_production / max_capacity # Calculate EAF - effective_available_hours = - total_equivalent_derate_hours + effective_available_hours = available_hours - total_equivalent_derate_hours return (effective_available_hours / period_hours) * 100 if period_hours > 0 else 0, total_equivalent_derate_hours except Exception as e: print("Error calculating EAF:", str(e))