Compare commits

..
228 Commits
Author SHA1 Message Date
Dylan Jeannin cfbc2d96a1 Mage: Fix parsing for .LAT files 2026-07-03 13:07:10 +02:00
parouby 64a26e987e ci: Fix again. 2026-06-25 15:48:41 +02:00
parouby eb233847de ci: Fix pipeline for release. 2026-06-25 15:42:46 +02:00
parouby 7bb810d427 Pollutants: Fix 'enabled' column update version. 2026-06-25 15:00:41 +02:00
Dylan Jeannin e49724072b Merge branch 'dev_dylan' into scenarios 2026-06-25 14:41:24 +02:00
Dylan Jeannin 3664ba93e9 Results: Fix open mage results from AdisTS solver 2026-06-25 14:39:45 +02:00
Dylan Jeannin a9903e987b AdisTS: Resolve bug with node ids not matching in config files 2026-06-25 14:05:04 +02:00
Dylan Jeannin f9a88a211c Fix pep8 2026-06-22 17:10:57 +02:00
Dylan Jeannin 32535af3e6 UpdateRK: avoid crash when updating RK with missing points on a section, warning message instead 2026-06-22 17:04:39 +02:00
Dylan Jeannin fa8948c1b6 Meshing: Debug error caused by index out of range when adding an empty transect on a reach 2026-06-22 15:18:08 +02:00
Dylan Jeannin 2d5455c09d Meshing: Debug crashing case when divide by 0 2026-06-22 14:16:33 +02:00
Dylan Jeannin 83680bbe57 Pollutant: quickfix, back to previous version 2026-06-22 12:12:02 +02:00
Dylan Jeannin 1d1a00a0ff Pollutants: merge conflicts 2026-06-22 10:42:29 +02:00
Dylan Jeannin ffd95b9fdf Pollutants: merge conflicts 2026-06-22 10:42:07 +02:00
parouby 32cbc6bc3e Results: Fix additional geotiff display. 2026-06-16 17:28:55 +02:00
parouby 7d7a05c04c Results: Minor change. 2026-06-16 16:28:46 +02:00
parouby 6ac268b668 Results: Add data: Move update to plot class. 2026-06-16 16:24:05 +02:00
parouby 19c72892a8 Results: Add data: Hydrogram: Move drawing to plot. 2026-06-16 16:13:10 +02:00
parouby 9717968e08 Mage: LC: Fix export. 2026-06-10 14:26:05 +02:00
parouby e19c3229bd Solver: Mage: HS: Minor change. 2026-06-05 17:01:22 +02:00
parouby b3c6f1031b Solver: Mage: Minor change. 2026-06-05 16:35:57 +02:00
parouby c2d93b4a4a MainWindow: Finally connect action for the 'Quit' menu action. 2026-06-05 15:56:25 +02:00
parouby 76430322dd Results: Minor change. 2026-06-05 14:59:48 +02:00
parouby 31ae0ab8e0 Study: Add minor comment. 2026-06-05 10:54:51 +02:00
parouby 8182d434ec GeoTIFF: Fix crashes at 'cannot remove artist'. 2026-06-05 10:01:53 +02:00
parouby da78d0cdb1 AdisTS: Fix mage result workdir and add scenario into paths. 2026-06-04 15:21:03 +02:00
parouby 850db144ac WaitingDialog: Fix windows size. 2026-06-04 14:53:43 +02:00
parouby 0f6709075f Scenarios: Merge branch 'master' into scenarios. 2026-06-04 14:03:49 +02:00
parouby e86b84b0b9 AdisTS: Results: Fix db save. 2026-06-03 16:00:12 +02:00
parouby e08d212986 AdisTS: D90: Fix reach pid update. 2026-06-03 15:34:52 +02:00
parouby 7b88d955e5 Geometry: Update RK refactoring and fix modified flag. 2026-06-02 16:46:30 +02:00
parouby d1c34fb437 Pamhyr: Fix PEP8. 2026-06-02 15:40:51 +02:00
parouby 611f9cdfa3 Trad: Minor change. 2026-06-02 15:16:50 +02:00
parouby bd2fc22e96 Results: Fix non valid results save. 2026-06-02 15:11:07 +02:00
parouby efa16837d8 AdisTS: Minor change. 2026-05-28 17:09:02 +02:00
parouby db7aad26fd Geometry: Point: Optimize loading with indexing and update db to 0.2.5. 2026-05-28 17:09:02 +02:00
parouby 229b3da2d1 AdistTS: Fix update with table exists tests. 2026-05-28 17:09:02 +02:00
Dylan Jeannin 48d6728e1e Enable pollutant: fix the list to browse to enable a pol 2026-05-28 15:11:06 +02:00
Dylan Jeannin c51c06e6ec DB version: Upgrade to add enabled in pollutants db 2026-05-28 15:02:40 +02:00
Dylan Jeannin a425c9a6ac BoundaryConditions: delete setter/getter duplicated for d50 and sigma 2026-05-28 15:01:36 +02:00
Dylan Jeannin e79ae8ebf5 Translation: new version of .ts file, with type of pollutants added 2026-05-28 13:01:48 +02:00
Dylan Jeannin e3020d15ba Merge branch 'scenarios' into dev_dylan 2026-05-28 11:56:00 +02:00
Dylan Jeannin 83e0ecbdc7 PEP8 fix 2026-05-28 11:54:44 +02:00
Dylan Jeannin 3087023caa Pollutants: Add translation for type of pol 2026-05-28 11:52:43 +02:00
parouby 7e080767da AdistTS: d90: Fix update to 0.2.*. 2026-05-28 10:27:05 +02:00
parouby 44c3a91d72 AdisTS: Fix db version update (again) for 0.0.11 2026-05-28 10:27:05 +02:00
Dylan Jeannin 26dc7d8761 Merge branch 'scenarios' of https://gitlab.com/pamhyr/pamhyr2 into scenarios 2026-05-27 09:35:46 +02:00
Dylan Jeannin e573fa4197 Pollutants: specific way to check if we modified the data in the type combobox 2026-05-27 09:35:10 +02:00
parouby 7aa0cf4361 AdisTS: Fix db update. 2026-05-26 18:00:05 +02:00
parouby c3faf45f75 SolverLogFileWindow: Add exeption to fix crash for #48. 2026-05-26 16:36:27 +02:00
Dylan Jeannin fb8b5aa77f Translation: back to old fr.ts version which broke translation* 2026-05-26 14:56:31 +02:00
Dylan Jeannin 8183a284af Merge branch 'pollutant_type_combobox' into dev_dylan 2026-05-26 12:27:44 +02:00
Dylan Jeannin 09997a0074 Tables: Skip table undo commands when data is unchanged (avoid data duplicates in db) 2026-05-26 12:27:41 +02:00
Dylan Jeannin c44597dfbd Merge branch 'dev_dylan' into scenarios 2026-05-26 11:15:32 +02:00
Dylan Jeannin b1632eaea5 Fix PEP8 2026-05-26 11:14:57 +02:00
Dylan Jeannin a2461af084 Scenario: avoid marking loaded scenario data as modified, that created unwanted copies in database 2026-05-26 11:12:36 +02:00
parouby 5c366839f3 Results: Fix db save where results do not correspond to scenario. 2026-05-26 10:28:41 +02:00
parouby 311df46a2c Friction: Fix save and delete method. 2026-05-26 10:28:41 +02:00
Dylan Jeannin a235f61044 PEP8 fixes 2026-05-26 08:15:04 +02:00
Dylan Jeannin 0b80b86d31 Merge branch 'dev_dylan' into scenarios 2026-05-26 08:10:34 +02:00
Dylan Jeannin 0c7ede5f43 Network: Add missing () on method to check if study is editable 2026-05-26 08:06:07 +02:00
Dylan Jeannin 445c1ac7ba Scenario: Add ⛔ symbol on missing windows when we are not in a leaf scenario 2026-05-26 07:42:08 +02:00
Dylan Jeannin c687e05d8f Pollutant types: Add type combobox with readable names 2026-05-22 18:36:28 +02:00
Dylan Jeannin 5fb6fecc24 Scenarios: Set parent to EDIT when deleting current scenario 2026-05-22 17:25:44 +02:00
Dylan Jeannin 3dfbdc328e PEP8 fix 2026-05-22 16:54:35 +02:00
Dylan Jeannin 97d87f2455 Merge branch 'scenarios' of https://gitlab.com/pamhyr/pamhyr2 into scenarios 2026-05-22 16:52:50 +02:00
Dylan Jeannin a78be47e2b AdisTS: Fix the possibility to edit AdisTS windows on a parent scenario 2026-05-22 16:47:02 +02:00
Dylan Jeannin eed2ce4243 Pollutants: Fix setters from undo/redo commands on edition 2026-05-22 15:25:11 +02:00
parouby 78ba7b70b4 AdisTS: OutputRK: Fix save methode. 2026-05-22 14:53:38 +02:00
Dylan Jeannin f86cd4602c Merge branch 'scenarios' into dev_dylan 2026-05-22 14:38:52 +02:00
Dylan Jeannin ee28afac86 HydraulicStructures: Fix hydraulic structures plot with missing input section 2026-05-22 12:57:34 +02:00
Dylan Jeannin 81ff6372e9 LateralSources: Debug of crash on RK selection 2026-05-22 12:45:27 +02:00
parouby 61156f1c3e Scenario: Fix scenario duplication. 2026-05-22 11:51:18 +02:00
parouby e26b170b05 Network: Fix reach selection where the study is read only. 2026-05-22 10:57:49 +02:00
Dylan Jeannin ddbaa0fb94 Pollutants: Fixed pollutant deletion removing the absolute index instead of the relative index 2026-05-22 10:53:07 +02:00
parouby 64d2b2678a Geometry: Profiles: Fix shift methode. 2026-05-22 10:39:10 +02:00
parouby 3a3b5ea2dd PamhyrDB: Minor change. 2026-05-22 10:09:34 +02:00
parouby 7cb3ac8a80 Clean code: Remove some print function call. 2026-05-22 09:42:32 +02:00
parouby 190ca5d171 Scenario: Add some save dialog. 2026-05-22 09:22:35 +02:00
parouby 5d8ec51417 Scenario: Remove posibility to remove non leaf scenario. 2026-05-22 09:21:55 +02:00
parouby d922436ada Scenario: Add Discard option to scenario switching dialog. 2026-05-21 16:26:51 +02:00
parouby 4537a829af WaitingDialog: Try fix deadlock. 2026-05-21 16:26:47 +02:00
parouby 363b9c7132 Scenario: Add HS to memory clean and minor comment change. 2026-05-21 15:07:18 +02:00
parouby f9d83ac68c Scenario: Add memory clean for Pamhyr list, dict and reaches. 2026-05-21 11:17:45 +02:00
Dylan Jeannin 63b5cbaba7 debug pollutants model and save in db 2026-05-20 17:17:51 +02:00
Dylan Jeannin 1989914aef error in the name of a db table 2026-05-20 10:48:37 +02:00
Dylan Jeannin 2c0b155baf error in the name of a db table 2026-05-20 10:40:11 +02:00
Dylan Jeannin 4d20c2457a pep8 fix 2026-05-20 10:19:52 +02:00
Dylan Jeannin 2dd9003721 merge scenario (missing tables) 2026-05-20 10:18:56 +02:00
Dylan Jeannin 94ceb44fd4 debug load study after deleting a node in the child scenario network 2026-05-20 09:51:38 +02:00
Dylan Jeannin ab50d2adf4 little fixes with variable names 2026-05-19 18:00:39 +02:00
Dylan Jeannin 0eb979256f little fixes with variable names 2026-05-19 17:59:30 +02:00
parouby 5ce94a63d3 Scenario: Clean DB add minimal comment. 2026-05-19 17:54:28 +02:00
parouby 42ea50d62e Study: Minor change. 2026-05-19 16:51:57 +02:00
parouby 0df4cf0fd5 Scenario: Clean DB. 2026-05-19 16:13:30 +02:00
Dylan Jeannin ccf3da114b fix duplication of same objects in db with when saving a child scenario 2026-05-19 16:13:21 +02:00
Dylan Jeannin 348417977d add missing tables in drop_all function, called on a scenario deletion 2026-05-19 15:58:37 +02:00
parouby 914a1f8866 Scenario: Add DB clean methode (not tested yet). 2026-05-19 12:22:20 +02:00
Dylan Jeannin cbbbeeac0b correct error that made InitialCondition addition bug 2026-05-19 11:40:03 +02:00
Dylan Jeannin c9ea726bfb correct error that made LateralContrib deletion bug 2026-05-19 11:30:38 +02:00
parouby fa88e05d91 AdisTS: BC: Fix dual sub classes dependencies. 2026-05-19 10:15:23 +02:00
parouby a6915020cb BC: Minor change. 2026-05-19 09:46:46 +02:00
Dylan Jeannin ea2287215e fix pep8 before push 2026-05-18 15:33:36 +02:00
Dylan Jeannin e940dead70 Merge branch 'dev_dylan' into scenarios 2026-05-18 15:04:37 +02:00
Theophile Terraz b6a6d54a22 Merge branch 'scenarios' of gitlab.com:pamhyr/pamhyr2 into scenarios 2026-05-12 16:17:32 +02:00
Theophile Terraz 11a95cb391 debug 2026-05-12 16:16:56 +02:00
Dylan Jeannin 084c22836b debug undo after deletion of BCA 2026-05-12 13:48:14 +02:00
Dylan Jeannin 726b81bf12 debug undo after add LateralContributionAdisTS 2026-05-12 08:40:35 +02:00
Dylan Jeannin 7c3de520d9 delete old references to pollutants in BoundaryConditionsAdisTS window 2026-05-12 08:39:58 +02:00
Dylan Jeannin b450796df5 stab window InitialCond AdisTS (undo / redo / add / delete / save / load), all should work correctly 2026-05-11 16:14:35 +02:00
Dylan Jeannin b1daadf9f2 rename variables for DIF to keep consistency 2026-05-11 14:59:48 +02:00
Dylan Jeannin c38e3ee446 correct pep8 rules 2026-05-11 14:59:14 +02:00
Dylan Jeannin 606b9919e3 stab window D90 AdisTS (undo / redo / add / delete / save / load), all should work correctly 2026-05-11 14:58:42 +02:00
Dylan Jeannin e3de7e7100 rename variable to keep consistency 2026-05-11 14:07:38 +02:00
Dylan Jeannin 92fae0e13f rename variable to keep consistency 2026-05-11 14:06:07 +02:00
Dylan Jeannin a8b260ad38 debug crash when double clic on PK combobox when no reach is selected (DIF window) 2026-05-11 13:49:54 +02:00
Dylan Jeannin faff877c80 stab window DIF AdisTS (undo / redo / add / delete / save / load), all should work correctly 2026-05-11 12:52:33 +02:00
Theophile Terraz 0a0eeb3c3f debug 2026-05-07 17:01:22 +02:00
parouby cdcbbc2cf4 doc: Remove watermark. 2026-05-06 11:03:56 +02:00
Dylan Jeannin 7565d8ee95 fix pep8 2026-05-05 17:51:49 +02:00
Dylan Jeannin 11e1302e05 fix add/del/undo/redo in edit BoundaryConditionsAdisTS + quick fix in edit LateralContributionsAdisTS 2026-05-05 17:50:45 +02:00
Dylan Jeannin 99cb2883a1 display seconds in LateralContributionAdisTS data (edition window) 2026-05-05 12:34:44 +02:00
Dylan Jeannin e61dede7f8 add Dylan in contributors 2026-05-05 12:33:41 +02:00
Dylan Jeannin cb4dfab482 conflicts resolution before merge 2026-05-05 10:50:42 +02:00
Dylan Jeannin 377c508038 correction pep8 2026-05-05 10:29:13 +02:00
Dylan Jeannin 30e34b6847 debug add function and save/load in db for edit LCAdisTS window 2026-05-05 10:28:30 +02:00
Dylan Jeannin 102702e1df fix delete function for BCAdisTS 2026-05-04 14:52:31 +02:00
Dylan Jeannin c014f0ef34 erase old and useless methods 2026-05-04 14:51:34 +02:00
Dylan Jeannin e615a6b9d4 debug index matching in delete function for LCAdisTS 2026-05-04 13:03:18 +02:00
Dylan Jeannin f1dca727c6 rename edition BCadisTS and LCadisTS windows 2026-05-04 11:11:23 +02:00
Dylan Jeannin 2db1f4c7f7 fix tab elts related to pollutants, and fix add/del functionnality for LateralContributionAdisTS 2026-04-30 15:56:11 +02:00
Dylan Jeannin b3d631943c correction bug with saving data for lateral contributions adisTS 2026-04-29 15:33:00 +02:00
Dylan Jeannin b6895ae99c correction bug with saving data for lateral contributions adisTS 2026-04-29 14:09:34 +02:00
Dylan Jeannin 9f26a7323c correction bug with saving data for lateral contributions adisTS 2026-04-29 14:03:58 +02:00
Dylan Jeannin 2a7e322ee5 fix name of window InitCondAdisTS to mention pollutant name 2026-04-28 16:44:33 +02:00
Dylan Jeannin 13a44264ac fix pep8 rules 2026-04-28 16:05:40 +02:00
Dylan Jeannin 6165719829 warning popup when export format for geometry reach is not .st 2026-04-28 14:30:40 +02:00
Dylan Jeannin 51dd6d27c5 try fix ci pipeline 2026-04-28 13:31:39 +02:00
Dylan Jeannin cea4dcd9cb refacto variable names for D90 2026-04-28 11:06:57 +02:00
Dylan Jeannin e1c1f411c3 fix bug when add button didnt refresh the tab list in Initial conditions AdisTS and d90 2026-04-28 11:06:57 +02:00
Dylan Jeannin 4550fc95d7 debug delete functionnality in InitialConditionsAdisTS 2026-04-28 11:06:57 +02:00
Dylan Jeannin 1f97af9c03 clean useless data 2026-04-28 11:06:57 +02:00
Dylan Jeannin df9e1ec42d fix delete for pollutants D90 2026-04-28 11:06:57 +02:00
Dylan Jeannin ac79fae1fb debug save db d90 2026-04-28 11:06:57 +02:00
Dylan Jeannin fe39f07981 consistency with called functions in the file 2026-04-28 11:06:57 +02:00
Dylan Jeannin b578e0c50c désactivation temporaire de undo dans la fenêtre résultat qui fait crash Pamhyr 2026-04-28 11:06:57 +02:00
Dylan Jeannin 4408a8f368 debug save BoundaryConditionsAdisTS 2026-04-28 11:06:57 +02:00
Dylan Jeannin d3e922af6e desactivation du clic sur le graph du réseau hors de l'onglet d'édition du réseau (ex. BoundaryConditions...) 2026-04-28 11:06:57 +02:00
Dylan Jeannin 8adf878584 garde le zoom sur un graph après rafraichissement des données 2026-04-28 11:06:57 +02:00
Dylan Jeannin aa6973813f debug fonction SAVE des figures, qui utilisait une méthode Matplotlib, plutôt qu'une méthode native Qt 2026-04-28 11:06:57 +02:00
Dylan Jeannin e21a64fcad zoom plus intuitif avec translation du widget pour garder une cohérence dans le zoom 2026-04-28 11:06:57 +02:00
Dylan Jeannin 8b6308d3c9 inversion du sens de zoom avec la molette sur les graphs (contrintuitif auparavant) 2026-04-28 11:06:57 +02:00
Dylan Jeannin bc3ea9c4b9 ajout automatique de l'extansion .st lorsque l'on ne le stipule pas manuellement dans l'export des tronçons 2026-04-28 11:06:57 +02:00
Dylan Jeannin 04ea55e21a can use '.' caracter and not only ',' in Shift Geometry window 2026-04-28 11:06:57 +02:00
Dylan Jeannin a68270ff67 update model + view to display only BCAdisTS related to pollutant, and not the global list, todo: reconnect add/del buttons 2026-04-28 11:06:57 +02:00
Dylan Jeannin c498ed2d96 add d50/sigma in database + load/save data in UI 2026-04-28 11:06:57 +02:00
Dylan Jeannin e6d059bc19 correction erreur identifiant bdd 2026-04-28 11:06:57 +02:00
Dylan Jeannin cc500ceccf debug model InitialConditionsAdisTSSpec 2026-04-28 11:06:57 +02:00
Dylan Jeannin 7a91969f41 refacto variable names for D90 2026-04-28 10:27:19 +02:00
Theophile Terraz 6bd3605d10 update doc links 2026-04-28 10:13:43 +02:00
Theophile Terraz 6c6da90952 debug QSO export 2026-04-28 10:09:49 +02:00
Theophile Terraz 539b63f765 update doc links 2026-04-28 10:09:24 +02:00
Dylan Jeannin e755459004 Merge branch 'scenarios' into dev_dylan 2026-04-27 16:54:52 +02:00
Dylan Jeannin a8f95e2386 fix bug when add button didnt refresh the tab list in Initial conditions AdisTS and d90 2026-04-27 16:45:36 +02:00
Dylan Jeannin 55b200a48e debug delete functionnality in InitialConditionsAdisTS 2026-04-27 16:32:41 +02:00
Dylan Jeannin 6b8d7c2df2 clean useless data 2026-04-27 15:44:57 +02:00
Dylan Jeannin eb7c4895ca fix delete for pollutants D90 2026-04-27 15:43:32 +02:00
Dylan Jeannin a060dbd461 debug save db d90 2026-04-27 12:00:39 +02:00
Dylan Jeannin 9379f7a27d consistency with called functions in the file 2026-04-27 10:07:36 +02:00
Theophile Terraz 19fedc2278 debug QSO export 2026-04-23 09:36:36 +02:00
Theophile Terraz 260efc17a9 pep8 + debug 2026-04-23 09:27:29 +02:00
Dylan Jeannin a4026d6386 désactivation temporaire de undo dans la fenêtre résultat qui fait crash Pamhyr 2026-04-22 13:45:15 +02:00
Dylan Jeannin d6f7755d12 debug save BoundaryConditionsAdisTS 2026-04-22 12:05:28 +02:00
Theophile Terraz 00b89635f3 add a warning window when opening Adis-TS results from file if wrong directory 2026-04-22 11:46:25 +02:00
Theophile Terraz f54dc07432 add warning windows in compare results 2026-04-22 11:38:51 +02:00
Theophile Terraz 3bbc55b4be add a warning window when opening Adis-TS results from file if wrong directory 2026-04-22 11:26:05 +02:00
Dylan Jeannin b58d3e005d desactivation du clic sur le graph du réseau hors de l'onglet d'édition du réseau (ex. BoundaryConditions...) 2026-04-21 16:55:04 +02:00
Dylan Jeannin 870bb41ff7 garde le zoom sur un graph après rafraichissement des données 2026-04-21 16:02:37 +02:00
Dylan Jeannin 0701531d37 debug fonction SAVE des figures, qui utilisait une méthode Matplotlib, plutôt qu'une méthode native Qt 2026-04-21 10:41:34 +02:00
Dylan Jeannin 43eb486700 zoom plus intuitif avec translation du widget pour garder une cohérence dans le zoom 2026-04-21 10:20:24 +02:00
Dylan Jeannin 82eeb9a478 inversion du sens de zoom avec la molette sur les graphs (contrintuitif auparavant) 2026-04-21 01:22:08 +02:00
Dylan Jeannin 432c263cdf ajout automatique de l'extansion .st lorsque l'on ne le stipule pas manuellement dans l'export des tronçons 2026-04-20 23:01:04 +02:00
Dylan Jeannin c2d28b0110 can use '.' caracter and not only ',' in Shift Geometry window 2026-04-16 16:44:14 +02:00
Dylan Jeannin 23361d142e update model + view to display only BCAdisTS related to pollutant, and not the global list, todo: reconnect add/del buttons 2026-04-16 15:14:28 +02:00
Dylan Jeannin 63836527c7 add d50/sigma in database + load/save data in UI 2026-04-13 13:10:50 +02:00
Dylan Jeannin 4f6e8249e0 correction erreur identifiant bdd 2026-04-08 14:33:02 +02:00
Dylan Jeannin ab2f775b95 debug model InitialConditionsAdisTSSpec 2026-04-08 12:34:05 +02:00
Theophile Terraz bb0e176956 debug 2026-04-07 11:48:36 +02:00
Theophile Terraz c0d4aced5e debug 2026-04-07 11:47:45 +02:00
Theophile Terraz 4ed865759a debug 2026-04-03 15:23:47 +02:00
Theophile Terraz 70e09d3b3a debug export csv 2026-04-03 14:42:58 +02:00
Theophile Terraz 2bcfa4d121 debug compare results with bedload 2026-04-03 14:42:58 +02:00
Theophile Terraz 5b497d61f0 add zfd in results 2026-04-03 14:41:30 +02:00
Theophile Terraz 242df7f648 start work on results buffers 2026-04-03 14:30:11 +02:00
Theophile Terraz 421e6797f6 debug export csv 2026-04-03 14:18:29 +02:00
Theophile Terraz 0886956ec8 debug compare results with bedload 2026-04-03 13:51:15 +02:00
Theophile Terraz 9670bde56f add zfd in results 2026-04-03 12:30:58 +02:00
Theophile Terraz 3277cf551b start work on results buffers 2026-04-02 11:54:55 +02:00
Theophile Terraz ad5cc831a3 debug bed elevation comparison 2026-03-26 11:02:46 +01:00
Theophile Terraz b00cca953e debug bed elevation comparison 2026-03-26 10:51:21 +01:00
Theophile Terraz 1f35020e3f update release to python3.12 2026-03-25 13:55:18 +01:00
Theophile Terraz 7ef41ad886 optimize results visu 2026-03-06 11:12:43 +01:00
Theophile Terraz ada396f26d debug 2026-03-05 16:46:22 +01:00
Theophile Terraz 674247db09 add talweg in update_rk 2026-03-04 11:14:05 +01:00
Theophile Terraz 5fecd29666 Claude François 2026-02-26 16:57:42 +01:00
Theophile Terraz 71e1324a0f debug open results from file 2026-02-24 17:19:59 +01:00
Theophile Terraz 07cae542b0 pep8 2026-02-24 09:18:47 +01:00
Theophile Terraz b693f67b99 debug RubarBE 2026-02-24 09:15:31 +01:00
Theophile Terraz 18a3d51d75 add adists with rubar3 + rubar3 in release 2026-02-23 10:46:05 +01:00
Theophile Terraz 118a39bd5c debug Rubar3 (from scenario branch) 2026-02-09 14:24:09 +01:00
Theophile Terraz 2009e8223b debug adists view 2026-01-30 11:32:06 +01:00
Theophile Terraz bdaa38ea5c Merge branch 'master' of gitlab.com:pamhyr/pamhyr2 2026-01-13 08:58:46 +01:00
Lionel Pénard 713732b1dd Fix doc links in README 2026-01-12 11:03:56 +01:00
Theophile Terraz f053539cdd debug 2025-12-19 11:29:02 +01:00
Theophile Terraz fc6916bb3e lot of debug 2025-12-12 14:18:09 +01:00
Theophile Terraz 97c26cb4fb debug HS 2025-12-12 11:33:13 +01:00
Theophile Terraz 10c98bb96e release for Jerome 2025-12-09 14:25:38 +01:00
Theophile Terraz 7c2d9891d0 debug IC 2025-12-05 16:51:39 +01:00
Theophile Terraz 3c3ac1f1df pep8 2025-12-05 10:19:43 +01:00
Theophile Terraz 80af278a0f add label in custom export results 2025-12-05 10:18:38 +01:00
Theophile Terraz f254eaee25 debug adists 2025-12-02 09:31:07 +01:00
Theophile Terraz 5adb0bdee5 debug GL in resluts 2025-11-28 15:31:07 +01:00
Theophile Terraz 64c99d46d4 debug named points in meshed profiles 2025-11-28 14:12:06 +01:00
Theophile Terraz 0f0defb36f Revert "debug ci"
This reverts commit 1e1ee67d4b.
2025-11-27 16:10:00 +01:00
Theophile Terraz 1e1ee67d4b debug ci 2025-11-27 16:03:09 +01:00
Theophile Terraz 5bc514c565 pep8 2025-11-26 11:58:58 +01:00
Theophile Terraz 1da3461dfe debug meshing 2025-11-26 11:16:19 +01:00
Theophile Terraz dd5eecb1e2 pep8 2025-11-24 15:18:28 +01:00
151 changed files with 3237 additions and 1396 deletions

No files matched your search

+3 -3
View File
@@ -186,7 +186,7 @@ unittest:
- pip3 install -r ./full-requirements.txt - pip3 install -r ./full-requirements.txt
- pip3 install -U -r ./full-requirements.txt - pip3 install -U -r ./full-requirements.txt
- cd src - cd src
- python3 -m unittest discover -t . - python3.12 -m unittest discover -t .
test-pep8: test-pep8:
stage: test stage: test
@@ -638,7 +638,7 @@ shadow-release:
- job: pkg-hash - job: pkg-hash
artifacts: true artifacts: true
rules: rules:
- if: $CI_COMMIT_BRANCH != 'master' && $CI_COMMIT_TAG - if: $CI_COMMIT_TAG && $CI_COMMIT_TAG !~ /^v/
artifacts: artifacts:
paths: paths:
- linux/pamhyr-gnulinux.tar.xz - linux/pamhyr-gnulinux.tar.xz
@@ -672,7 +672,7 @@ tag-release:
- job: pkg-hash - job: pkg-hash
artifacts: true artifacts: true
rules: rules:
- if: $CI_COMMIT_BRANCH == 'master' && $CI_COMMIT_TAG - if: $CI_COMMIT_TAG =~ /^v/
artifacts: artifacts:
paths: paths:
- linux/pamhyr-gnulinux.tar.xz - linux/pamhyr-gnulinux.tar.xz
+2 -1
View File
@@ -1,3 +1,4 @@
Sylvain COULIBALY, INRAE, 2020 - 2022 Sylvain COULIBALY, INRAE, 2020 - 2022
Théophile TERRAZ, INRAE, 2022 - 2024 Théophile TERRAZ, INRAE, 2022 - 2024
Pierre-Antoine ROUBY, INRAE, 2023 - 2025 Pierre-Antoine ROUBY, INRAE, 2023 - 2025
Dylan JEANNIN, INRAE, 2026
+8 -8
View File
@@ -3,7 +3,7 @@
Pamhyr is a free and open source graphical user interface for 1D hydro-sedimentary Pamhyr is a free and open source graphical user interface for 1D hydro-sedimentary
modelling of rivers. modelling of rivers.
![logo](https://gitlab.irstea.fr/theophile.terraz/pamhyr/-/raw/master/src/View/ui/ressources/Pamhyr2_logo.png) ![logo](https://gitlab.com/pamhyr/pamhyr2/-/raw/master/src/View/ui/ressources/Pamhyr2_logo.png)
## Features ## Features
@@ -21,9 +21,9 @@ modelling of rivers.
+ Create and edit study alternative scenario tree + Create and edit study alternative scenario tree
Let see the Let see the
[documentation](https://gitlab.irstea.fr/theophile.terraz/pamhyr/-/wikis/home) [documentation](https://gitlab.com/pamhyr/pamhyr2/-/wikis/home)
([:fr:](https://gitlab.irstea.fr/theophile.terraz/pamhyr/-/wikis/home-fr), ([:fr:](https://gitlab.com/pamhyr/pamhyr2/-/wikis/home-fr),
[:gb:](https://gitlab.irstea.fr/theophile.terraz/pamhyr/-/wikis/home-en)) [:gb:](https://gitlab.com/pamhyr/pamhyr2/-/wikis/home-en))
for more details. for more details.
## Install ## Install
@@ -31,11 +31,11 @@ for more details.
### GNU/Linux ### GNU/Linux
See documentation: See documentation:
- [French :fr:](https://gitlab.irstea.fr/theophile.terraz/pamhyr/-/wikis/home/fr/Install%20on%20GNULinux) - [French :fr:](https://gitlab.com/pamhyr/pamhyr2/-/wikis/home/fr/Install%20on%20GNULinux)
- [English :gb:](https://gitlab.irstea.fr/theophile.terraz/pamhyr/-/wikis/home/en/Install%20on%20GNULinux) - [English :gb:](https://gitlab.com/pamhyr/pamhyr2/-/wikis/home/en/Install%20on%20GNULinux)
### Windows ### Windows
See documentation: See documentation:
- [French :fr:](https://gitlab.irstea.fr/theophile.terraz/pamhyr/-/wikis/home/fr/Install%20on%20Windows) - [French :fr:](https://gitlab.com/pamhyr/pamhyr2/-/wikis/home/fr/Install%20on%20Windows)
- [English :gb:](https://gitlab.irstea.fr/theophile.terraz/pamhyr/-/wikis/home/en/Install%20on%20Windows) - [English :gb:](https://gitlab.com/pamhyr/pamhyr2/-/wikis/home/en/Install%20on%20Windows)
+6 -6
View File
@@ -157,12 +157,12 @@
%% %%
%% FIXME: Delete me for the first realease %% FIXME: Delete me for the first realease
\usepackage{draftwatermark} %% \usepackage{draftwatermark}
\SetWatermarkLightness{0.8} %% \SetWatermarkLightness{0.8}
\SetWatermarkAngle{25} %% \SetWatermarkAngle{25}
\SetWatermarkScale{3} %% \SetWatermarkScale{3}
\SetWatermarkFontSize{1cm} %% \SetWatermarkFontSize{1cm}
\SetWatermarkText{Work in progress} %% \SetWatermarkText{Work in progress}
%% Icons %% Icons
+1
View File
@@ -14,3 +14,4 @@ platformdirs>=4.2.0
pyshp>=2.3.1 pyshp>=2.3.1
rasterio==1.3.11 rasterio==1.3.11
#fortranformat==2.0.3 #fortranformat==2.0.3
+7 -4
View File
@@ -66,7 +66,6 @@ class InternalMeshing(AMeshingTool):
return new_profiles return new_profiles
def st_to_m(self, profiles, guide_list): def st_to_m(self, profiles, guide_list):
guide_list = ["un"] + guide_list + ["np"] guide_list = ["un"] + guide_list + ["np"]
max_values = [0] * (len(guide_list) - 1) max_values = [0] * (len(guide_list) - 1)
max_values_index = [0] * (len(guide_list) - 1) max_values_index = [0] * (len(guide_list) - 1)
@@ -85,11 +84,13 @@ class InternalMeshing(AMeshingTool):
profiles[isect+1], profiles[isect+1],
guide_list[i], guide_list[i],
guide_list[i+1]) guide_list[i+1])
for isect in reversed(range(0, max_values_index[i])): for isect in reversed(range(0, max_values_index[i])):
self.compl_sect(profiles[isect+1], self.compl_sect(profiles[isect+1],
profiles[isect], profiles[isect],
guide_list[i], guide_list[i],
guide_list[i+1]) guide_list[i+1])
return profiles return profiles
def interpolate_transversal_step(self, def interpolate_transversal_step(self,
@@ -129,6 +130,7 @@ class InternalMeshing(AMeshingTool):
p.name = f'interpol{p.rk}' p.name = f'interpol{p.rk}'
new_profiles2.append(p) new_profiles2.append(p)
new_profiles.append(new_profiles2) new_profiles.append(new_profiles2)
return new_profiles return new_profiles
def compl_sect(self, sect1, sect2, tag1, tag2): def compl_sect(self, sect1, sect2, tag1, tag2):
@@ -164,7 +166,6 @@ class InternalMeshing(AMeshingTool):
for i in range(len1): for i in range(len1):
ltot1 += sect1.point(start1+i).dist(sect1.point(start1+i+1)) ltot1 += sect1.point(start1+i).dist(sect1.point(start1+i+1))
alpha.append(ltot1) alpha.append(ltot1)
alpha = list(map(lambda x: x/ltot1, alpha)) # target ratios
for i in range(len2): for i in range(len2):
ltot2 += sect2.point(start2+i).dist(sect2.point(start2+i+1)) ltot2 += sect2.point(start2+i).dist(sect2.point(start2+i+1))
beta.append(ltot2) beta.append(ltot2)
@@ -179,6 +180,7 @@ class InternalMeshing(AMeshingTool):
sect2.add_npoints(len1-len2) sect2.add_npoints(len1-len2)
len2 = len1 len2 = len1
else: # regular case else: # regular case
alpha = list(map(lambda x: x/ltot1, alpha)) # target ratios
beta = list(map(lambda x: x/ltot2, beta)) # current ratios beta = list(map(lambda x: x/ltot2, beta)) # current ratios
for i in range(len1 - len2): for i in range(len1 - len2):
beta.append(1.0) beta.append(1.0)
@@ -214,7 +216,7 @@ class InternalMeshing(AMeshingTool):
ratio = (alpha[j0] - beta[j0-1]) \ ratio = (alpha[j0] - beta[j0-1]) \
/ (beta[j0] - beta[j0-1]) / (beta[j0] - beta[j0-1])
if ratio < 0.0: if ratio < 0.0:
print(f"ratio négatif {ratio}") logger.warning(f"ratio négatif {ratio}")
# on double le point a gauche # on double le point a gauche
p = sect2.point(start2+j0-1).copy() p = sect2.point(start2+j0-1).copy()
sect2.insert_point(start2+j0-1, p) sect2.insert_point(start2+j0-1, p)
@@ -243,8 +245,9 @@ class InternalMeshing(AMeshingTool):
sect2.point(start2+len2).name = '' sect2.point(start2+len2).name = ''
if tag1 != "un": if tag1 != "un":
sect2.point(start2).name = tag1 sect2.point(start2).name = tag1
if tag1 != "np": if tag2 != "np":
sect2.point(start2+len2).name = tag2 sect2.point(start2+len2).name = tag2
sect2.modified() sect2.modified()
def update_rk(self, reach, begin_rk, end_rk, def update_rk(self, reach, begin_rk, end_rk,
@@ -232,6 +232,8 @@ class BoundaryCondition(SQLSubModel):
type TEXT NOT NULL, type TEXT NOT NULL,
tab TEXT NOT NULL, tab TEXT NOT NULL,
node INTEGER, node INTEGER,
d50 REAL DEFAULT 0.002,
sigma REAL DEFAULT 1,
{Scenario.create_db_add_scenario()}, {Scenario.create_db_add_scenario()},
{Scenario.create_db_add_scenario_fk()}, {Scenario.create_db_add_scenario_fk()},
FOREIGN KEY(node) REFERENCES river_node(pamhyr_id), FOREIGN KEY(node) REFERENCES river_node(pamhyr_id),
@@ -258,6 +260,17 @@ class BoundaryCondition(SQLSubModel):
"ADD COLUMN deleted BOOLEAN NOT NULL DEFAULT FALSE" "ADD COLUMN deleted BOOLEAN NOT NULL DEFAULT FALSE"
) )
if major == "0" and int(minor) <= 2:
if int(release) < 4:
execute(
"ALTER TABLE boundary_condition " +
"ADD COLUMN d50 REAL DEFAULT 0.002"
)
execute(
"ALTER TABLE boundary_condition " +
"ADD COLUMN sigma REAL DEFAULT 1"
)
return cls._update_submodel(execute, version, data) return cls._update_submodel(execute, version, data)
@classmethod @classmethod
@@ -316,7 +329,8 @@ class BoundaryCondition(SQLSubModel):
return new return new
table = execute( table = execute(
"SELECT pamhyr_id, deleted, name, type, node, scenario " + "SELECT pamhyr_id, deleted, name, type, node, d50, sigma, " +
"scenario " +
"FROM boundary_condition " + "FROM boundary_condition " +
f"WHERE tab = '{tab}' " + f"WHERE tab = '{tab}' " +
f"AND scenario = {scenario.id} " + f"AND scenario = {scenario.id} " +
@@ -331,6 +345,8 @@ class BoundaryCondition(SQLSubModel):
name = next(it) name = next(it)
t = next(it) t = next(it)
node = next(it) node = next(it)
d50 = next(it)
sigma = next(it)
owner_scenario = next(it) owner_scenario = next(it)
ctor = cls._get_ctor_from_type(t) ctor = cls._get_ctor_from_type(t)
@@ -340,9 +356,14 @@ class BoundaryCondition(SQLSubModel):
status=data['status'], status=data['status'],
owner_scenario=owner_scenario owner_scenario=owner_scenario
) )
if deleted: if deleted:
bc.set_as_deleted() bc.set_as_deleted()
if t == "SL":
bc.d50 = d50
bc.sigma = sigma
bc.node = None bc.node = None
if node != -1: if node != -1:
bc.node = next(filter(lambda n: n.id == node, nodes), None) bc.node = next(filter(lambda n: n.id == node, nodes), None)
@@ -375,15 +396,22 @@ class BoundaryCondition(SQLSubModel):
if self._node is not None: if self._node is not None:
node = self._node.id node = self._node.id
d50 = 0.002
sigma = 1
if self._type == "SL":
d50 = self._d50
sigma = self._sigma
execute( execute(
"INSERT INTO " + "INSERT INTO " +
"boundary_condition(" + "boundary_condition(" +
"pamhyr_id, deleted, name, type, tab, node, scenario" + "pamhyr_id, deleted, name, type, tab, node, d50, sigma, scenario" +
") " + ") " +
"VALUES (" + "VALUES (" +
f"{self._pamhyr_id}, {self._db_format(self.is_deleted())}, " + f"{self._pamhyr_id}, {self._db_format(self.is_deleted())}, " +
f"'{self._db_format(self._name)}', " + f"'{self._db_format(self._name)}', " +
f"'{self._db_format(self._type)}', '{tab}', {node}, " + f"'{self._db_format(self._type)}', '{tab}', {node}, " +
f"{d50}, {sigma}, " +
f"{self._status.scenario_id}" + f"{self._status.scenario_id}" +
")" ")"
) )
@@ -468,6 +496,24 @@ class BoundaryCondition(SQLSubModel):
def has_node(self): def has_node(self):
return self._node is not None return self._node is not None
@property
def d50(self):
return self._d50
@d50.setter
def d50(self, value):
self._d50 = float(value)
self.modified()
@property
def sigma(self):
return self._sigma
@sigma.setter
def sigma(self, value):
self._sigma = float(value)
self.modified()
@property @property
def header(self): def header(self):
return self._header.copy() return self._header.copy()
@@ -70,9 +70,8 @@ class Data(SQLSubModel):
def _db_update(cls, execute, version, data=None): def _db_update(cls, execute, version, data=None):
major, minor, release = version.strip().split(".") major, minor, release = version.strip().split(".")
created = False created = False
if major == "0" and minor == "0":
if major == "0" and int(minor) <= 1: if int(release) < 11:
if int(release) < 7:
cls._db_create(execute) cls._db_create(execute)
created = True created = True
@@ -143,8 +142,9 @@ class Data(SQLSubModel):
table = execute( table = execute(
"SELECT pamhyr_id, deleted, data0, data1, scenario FROM " + "SELECT pamhyr_id, deleted, data0, data1, scenario FROM " +
"boundary_condition_data_adists " + "boundary_condition_data_adists " +
f"WHERE bca = {bca.id} " + f"WHERE scenario = {scenario.id} " +
f"AND scenario = {scenario.id}" # f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))}) " +
f"AND bca = {bca.id}"
) )
if table is not None: if table is not None:
@@ -187,8 +187,9 @@ class Data(SQLSubModel):
execute( execute(
"INSERT INTO " + "INSERT INTO " +
"boundary_condition_data_adists(" + "boundary_condition_data_adists(" +
"pamhyr_id, data0, data1, bca, scenario) " + "pamhyr_id, deleted, data0, data1, bca, scenario) " +
f"VALUES ({self.id}, '{data0}', {data1}, {bca.id}, " + f"VALUES ({self.id}, {self._db_format(self.is_deleted())}, " +
f"'{data0}', {data1}, {bca.id}, " +
f"{self._status.scenario_id})" f"{self._status.scenario_id})"
) )
@@ -247,15 +248,13 @@ class BoundaryConditionAdisTS(SQLSubModel):
major, minor, release = version.strip().split(".") major, minor, release = version.strip().split(".")
created = False created = False
if major == "0" and int(minor) <= 1: if major == "0" and int(minor) < 2:
if int(release) < 7: if cls.is_table_exists(execute, "boundary_condition_adists"):
cls._db_update_to_0_2_0(execute, data)
else:
cls._db_create(execute) cls._db_create(execute)
created = True created = True
if major == "0" and int(minor) < 2:
if not created:
cls._db_update_to_0_2_0(execute, data)
if not created: if not created:
return cls._update_submodel(execute, version, data) return cls._update_submodel(execute, version, data)
@@ -300,6 +299,14 @@ class BoundaryConditionAdisTS(SQLSubModel):
if pol_id == -1: if pol_id == -1:
continue continue
if pol_id not in pid_pol:
# ⚠️ cas important : probablement déjà migré
logger.warning(
f"pol_id {pol_id} not in pid_pol " +
"→ probably already migrated"
)
continue
execute( execute(
f"UPDATE boundary_condition_adists " + f"UPDATE boundary_condition_adists " +
f"SET pollutant = {pid_pol[pol_id]} " + f"SET pollutant = {pid_pol[pol_id]} " +
@@ -312,17 +319,21 @@ class BoundaryConditionAdisTS(SQLSubModel):
status = data['status'] status = data['status']
nodes = data['nodes'] nodes = data['nodes']
scenario = data["scenario"] scenario = data["scenario"]
pollutant = data.get("pollutant")
loaded = data['loaded_pid'] loaded = data['loaded_pid']
if scenario is None: if scenario is None:
return new return new
table = execute( sql = (
"SELECT pamhyr_id, deleted, pollutant, type, node, scenario " + "SELECT pamhyr_id, deleted, pollutant, type, node, scenario "
"FROM boundary_condition_adists " + "FROM boundary_condition_adists " +
f"WHERE scenario = {scenario.id} " + f"WHERE scenario = {scenario.id} "
f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))}) "
) )
if pollutant is not None:
sql += f"AND pollutant = {pollutant.id} "
table = execute(sql)
if table is not None: if table is not None:
for row in table: for row in table:
@@ -347,7 +358,7 @@ class BoundaryConditionAdisTS(SQLSubModel):
bc.type = bc_type bc.type = bc_type
bc.node = None bc.node = None
if row[3] != -1: if node != -1:
tmp = next( tmp = next(
filter( filter(
lambda n: n.id == node, nodes lambda n: n.id == node, nodes
@@ -426,7 +437,14 @@ class BoundaryConditionAdisTS(SQLSubModel):
d._data_traversal(predicate, modifier, data) d._data_traversal(predicate, modifier, data)
def __len__(self): def __len__(self):
return len(self._data) return len(
list(
filter(
lambda el: el is not None and not el.is_deleted(),
self._data
)
)
)
@classmethod @classmethod
def time_convert(cls, data): def time_convert(cls, data):
@@ -451,7 +469,7 @@ class BoundaryConditionAdisTS(SQLSubModel):
@node.setter @node.setter
def node(self, node): def node(self, node):
self._node = node self._node = node
self._status.modified() self.modified()
@property @property
def header(self): def header(self):
@@ -460,7 +478,7 @@ class BoundaryConditionAdisTS(SQLSubModel):
@header.setter @header.setter
def header(self, header): def header(self, header):
self._header = header self._header = header
self._status.modified() self.modified()
@property @property
def pollutant(self): def pollutant(self):
@@ -469,7 +487,7 @@ class BoundaryConditionAdisTS(SQLSubModel):
@pollutant.setter @pollutant.setter
def pollutant(self, pollutant): def pollutant(self, pollutant):
self._pollutant = pollutant self._pollutant = pollutant
self._status.modified() self.modified()
@property @property
def type(self): def type(self):
@@ -478,11 +496,16 @@ class BoundaryConditionAdisTS(SQLSubModel):
@type.setter @type.setter
def type(self, type): def type(self, type):
self._type = type self._type = type
self._status.modified() self.modified()
@property @property
def data(self): def data(self):
return self._data.copy() return list(
filter(
lambda el: el is not None and not el.is_deleted(),
self._data
)
)
@property @property
def _default_0(self): def _default_0(self):
@@ -511,33 +534,36 @@ class BoundaryConditionAdisTS(SQLSubModel):
return (new_0, new_1) return (new_0, new_1)
def add(self, index: int): def add(self, index: int):
value = (self._default_0, self._default_1) value = Data(
self._default_0, self._default_1,
status=self._status
)
self._data.insert(index, value) self._data.insert(index, value)
self._status.modified() self.modified()
return value return value
def insert(self, index: int, value): def insert(self, index: int, value):
self._data.insert(index, value) self._data.insert(index, value)
self._status.modified() self.modified()
def delete_i(self, indexes): def delete_i(self, indexes):
self._data = list( list(
map( map(
lambda e: e[1], lambda e: e[1].set_as_deleted(),
filter( filter(
lambda e: e[0] not in indexes, lambda e: e[0] in indexes,
enumerate(self.data) enumerate(self.data)
) )
) )
) )
self._status.modified() self.modified()
def sort(self, _reverse=False, key=None): def sort(self, _reverse=False, key=None):
if key is None: if key is None:
self._data.sort(reverse=_reverse) self._data.sort(reverse=_reverse)
else: else:
self._data.sort(reverse=_reverse, key=key) self._data.sort(reverse=_reverse, key=key)
self._status.modified() self.modified()
def index(self, bc): def index(self, bc):
self._data.index(bc) self._data.index(bc)
@@ -552,10 +578,10 @@ class BoundaryConditionAdisTS(SQLSubModel):
return lst return lst
def _set_i_c_v(self, index, column, value): def _set_i_c_v(self, index, column, value):
v = list(self._data[index]) v = self._data[index]
v[column] = self._types[column](value) v[column] = self._types[column](value)
self._data[index] = tuple(v) self._data[index] = v
self._status.modified() self.modified()
def set_i_0(self, index: int, value): def set_i_0(self, index: int, value):
self._set_i_c_v(index, 0, value) self._set_i_c_v(index, 0, value)
@@ -38,9 +38,13 @@ class BoundaryConditionsAdisTSList(PamhyrModelList):
if data is None: if data is None:
data = {} data = {}
new._lst = BoundaryConditionAdisTS._db_load( previous_pollutant = data.get("pollutant")
execute, data data.pop("pollutant", None)
)
new._lst = BoundaryConditionAdisTS._db_load(execute, data)
if previous_pollutant is not None:
data["pollutant"] = previous_pollutant
return new return new
+4 -6
View File
@@ -73,15 +73,13 @@ class D90AdisTS(SQLSubModel):
major, minor, release = version.strip().split(".") major, minor, release = version.strip().split(".")
created = False created = False
if major == "0" and int(minor) <= 1: if major == "0" and int(minor) < 2:
if int(release) < 6: if cls.is_table_exists(execute, "d90_adists"):
cls._db_update_to_0_2_0(execute, data)
else:
cls._db_create(execute) cls._db_create(execute)
created = True created = True
if major == "0" and int(minor) < 2:
if not created:
cls._db_update_to_0_2_0(execute, data)
if not created: if not created:
return cls._update_submodel(execute, version, data) return cls._update_submodel(execute, version, data)
+15 -15
View File
@@ -70,16 +70,12 @@ class D90AdisTSSpec(SQLSubModel):
@classmethod @classmethod
def _db_update(cls, execute, version, data=None): def _db_update(cls, execute, version, data=None):
major, minor, release = version.strip().split(".") major, minor, release = version.strip().split(".")
created = False
if major == 0 and minor < 1:
if int(release) < 6:
cls._db_create(execute)
created = True
if major == "0" and int(minor) < 2: if major == "0" and int(minor) < 2:
if not created: if cls.is_table_exists(execute, "d90_spec"):
cls._db_update_to_0_2_0(execute, data) cls._db_update_to_0_2_0(execute, data)
else:
cls._db_create(execute)
return True return True
@@ -87,6 +83,7 @@ class D90AdisTSSpec(SQLSubModel):
def _db_update_to_0_2_0(cls, execute, data): def _db_update_to_0_2_0(cls, execute, data):
table = "d90_spec" table = "d90_spec"
table_new = "d90_adists_spec" table_new = "d90_adists_spec"
reachs = data['id2pid']['river_reach']
cls.update_db_add_pamhyr_id(execute, table, data) cls.update_db_add_pamhyr_id(execute, table, data)
Scenario.update_db_add_scenario(execute, table) Scenario.update_db_add_scenario(execute, table)
@@ -95,8 +92,10 @@ class D90AdisTSSpec(SQLSubModel):
execute( execute(
f"INSERT INTO {table_new}_tmp " + f"INSERT INTO {table_new}_tmp " +
"(pamhyr_id, name, d90, enabled, scenario) " + "(pamhyr_id, d90_default, name, reach, " +
"SELECT pamhyr_id, name, d90, enabled, scenario " + " start_rk, end_rk, d90, enabled, scenario) " +
"SELECT pamhyr_id, d90_default, name, reach, " +
" start_rk, end_rk, d90, enabled, scenario " +
f"FROM {table}" f"FROM {table}"
) )
@@ -104,12 +103,13 @@ class D90AdisTSSpec(SQLSubModel):
execute(f"ALTER TABLE {table_new}_tmp RENAME TO {table_new}") execute(f"ALTER TABLE {table_new}_tmp RENAME TO {table_new}")
cls._db_update_to_0_2_0_set_d90_pid(execute, data) cls._db_update_to_0_2_0_set_d90_pid(execute, data)
cls._db_update_to_0_2_0_set_reach_pid(execute, table_new, reachs)
@classmethod @classmethod
def _db_update_to_0_2_0_set_d90_pid(cls, execute, data): def _db_update_to_0_2_0_set_d90_pid(cls, execute, data):
pid_d90 = data["id2pid"]["d90_adists"] pid_d90 = data["id2pid"]["d90_adists"]
els = execute( els = execute(
f"SELECT pamhyr_id, d90 FROM d90_adists_spec" f"SELECT pamhyr_id, d90_default FROM d90_adists_spec"
) )
for row in els: for row in els:
@@ -121,8 +121,8 @@ class D90AdisTSSpec(SQLSubModel):
continue continue
execute( execute(
f"UPDATE d90_adists_spec" + f"UPDATE d90_adists_spec " +
f"SET d90 = {pid_d90[d90_id]} " + f"SET d90_default = {pid_d90[d90_id]} " +
f"WHERE pamhyr_id = {pid}" f"WHERE pamhyr_id = {pid}"
) )
@@ -167,7 +167,7 @@ class D90AdisTSSpec(SQLSubModel):
owner_scenario=owner_scenario owner_scenario=owner_scenario
) )
if deleted: if deleted:
new_spec.is_deleted() new_spec.set_as_deleted()
new_spec.reach = reach new_spec.reach = reach
new_spec.start_rk = start_rk new_spec.start_rk = start_rk
@@ -196,12 +196,12 @@ class D90AdisTSSpec(SQLSubModel):
"d90_default, name, reach, " + "d90_default, name, reach, " +
"start_rk, end_rk, d90, enabled, scenario) " + "start_rk, end_rk, d90, enabled, scenario) " +
"VALUES (" + "VALUES (" +
f"{self.id}, {self._db_format(self.is_deleted())}" + f"{self.id}, {self._db_format(self.is_deleted())}, " +
f"{d90_default}, " + f"{d90_default}, " +
f"'{self._db_format(self._name_section)}', " + f"'{self._db_format(self._name_section)}', " +
f"{self._reach}, " + f"{self._reach}, " +
f"{self._start_rk}, {self._end_rk}, " + f"{self._start_rk}, {self._end_rk}, " +
f"{self._d90}, {self._enabled}" + f"{self._d90}, {self._enabled}, " +
f"{self._status.scenario_id}" + f"{self._status.scenario_id}" +
")" ")"
) )
+14 -16
View File
@@ -79,15 +79,13 @@ class DIFAdisTS(SQLSubModel):
major, minor, release = version.strip().split(".") major, minor, release = version.strip().split(".")
created = False created = False
if major == "0" and int(minor) <= 1: if major == "0" and int(minor) < 2:
if int(release) < 6: if cls.is_table_exists(execute, "dif_adists"):
cls._db_update_to_0_2_0(execute, data)
else:
cls._db_create(execute) cls._db_create(execute)
created = True created = True
if major == "0" and int(minor) < 2:
if not created:
cls._db_update_to_0_2_0(execute, data)
if not created: if not created:
return cls._update_submodel(execute, version, data) return cls._update_submodel(execute, version, data)
@@ -142,24 +140,24 @@ class DIFAdisTS(SQLSubModel):
enabled = (next(it) == 1) enabled = (next(it) == 1)
owner_scenario = next(it) owner_scenario = next(it)
dif = cls( DIF = cls(
id=pid, id=pid,
name=name, name=name,
status=status, status=status,
owner_scenario=owner_scenario, owner_scenario=owner_scenario,
) )
dif.method = method DIF.method = method
dif.dif = dif DIF.dif = dif
dif.b = b DIF.b = b
dif.c = c DIF.c = c
dif.enabled = enabled DIF.enabled = enabled
data['dif_default_id'] = pid data['dif_default_id'] = pid
dif._data = DIFAdisTSSpec._db_load(execute, data) DIF._data = DIFAdisTSSpec._db_load(execute, data)
loaded.add(pid) loaded.add(pid)
new.append(dif) new.append(DIF)
data["scenario"] = scenario.parent data["scenario"] = scenario.parent
new += cls._db_load(execute, data) new += cls._db_load(execute, data)
@@ -190,7 +188,7 @@ class DIFAdisTS(SQLSubModel):
b = self.b b = self.b
c = -1. c = -1.
if self.dif is not None: if self.c is not None:
c = self.c c = self.c
sql = ( sql = (
@@ -201,7 +199,7 @@ class DIFAdisTS(SQLSubModel):
"VALUES (" + "VALUES (" +
f"{self.id}, '{self._db_format(self._name)}', " + f"{self.id}, '{self._db_format(self._name)}', " +
f"'{self._db_format(self._method)}', " + f"'{self._db_format(self._method)}', " +
f"{dif.id}, {b}, {c}, {self._enabled}, " + f"{dif}, {b}, {c}, {self._enabled}, " +
f"{self._status.scenario_id}" + f"{self._status.scenario_id}" +
")" ")"
) )
+13 -20
View File
@@ -33,16 +33,14 @@ class DIFAdisTSSpec(SQLSubModel):
_id_cnt = 0 _id_cnt = 0
def __init__(self, id: int = -1, method: str = "", def __init__(self, id: int = -1, method: str = "",
status=None): status=None, owner_scenario=-1):
super(DIFAdisTSSpec, self).__init__() super(DIFAdisTSSpec, self).__init__(
id=id, status=status,
owner_scenario=owner_scenario
)
self._status = status self._status = status
if id == -1:
self.id = DIFAdisTSSpec._id_cnt
else:
self.id = id
self._method = method self._method = method
self._reach = None self._reach = None
self._start_rk = None self._start_rk = None
@@ -52,8 +50,6 @@ class DIFAdisTSSpec(SQLSubModel):
self._c = None self._c = None
self._enabled = True self._enabled = True
DIFAdisTSSpec._id_cnt = max(DIFAdisTSSpec._id_cnt + 1, self.id)
@classmethod @classmethod
def _db_create(cls, execute, ext=""): def _db_create(cls, execute, ext=""):
execute(f""" execute(f"""
@@ -81,16 +77,12 @@ class DIFAdisTSSpec(SQLSubModel):
@classmethod @classmethod
def _db_update(cls, execute, version, data=None): def _db_update(cls, execute, version, data=None):
major, minor, release = version.strip().split(".") major, minor, release = version.strip().split(".")
created = False
if major == 0 and minor < 1:
if int(release) < 6:
cls._db_create(execute)
created = True
if major == "0" and int(minor) < 2: if major == "0" and int(minor) < 2:
if not created: if cls.is_table_exists(execute, "dif_spec"):
cls._db_update_to_0_2_0(execute, data) cls._db_update_to_0_2_0(execute, data)
else:
cls._db_create(execute)
return True return True
@@ -195,7 +187,7 @@ class DIFAdisTSSpec(SQLSubModel):
new_spec.c = c new_spec.c = c
new_spec.enabled = enabled new_spec.enabled = enabled
loaded.add(pid) loaded.add(id)
new.append(new_spec) new.append(new_spec)
data["scenario"] = scenario.parent data["scenario"] = scenario.parent
@@ -210,22 +202,23 @@ class DIFAdisTSSpec(SQLSubModel):
dif_default = data['dif_default_id'] dif_default = data['dif_default_id']
execute( sql = (
"INSERT INTO " + "INSERT INTO " +
"dif_adists_spec(pamhyr_id, deleted, " + "dif_adists_spec(pamhyr_id, deleted, " +
"dif_default, method, reach, " + "dif_default, method, reach, " +
"start_rk, end_rk, dif, b, c, enabled, scenario) " + "start_rk, end_rk, dif, b, c, enabled, scenario) " +
"VALUES (" + "VALUES (" +
f"{self.id}, {self._db_format(self.is_deleted())}" + f"{self.id}, {self._db_format(self.is_deleted())}, " +
f"{dif_default}, " + f"{dif_default}, " +
f"'{self._db_format(self._method)}', " + f"'{self._db_format(self._method)}', " +
f"{self._reach}, " + f"{self._reach}, " +
f"{self._start_rk}, {self._end_rk}, " + f"{self._start_rk}, {self._end_rk}, " +
f"{self._dif}, {self._b}, {self._c}, " + f"{self._dif}, {self._b}, {self._c}, " +
f"{self._enabled}" + f"{self._enabled}, " +
f"{self._status.scenario_id}" + f"{self._status.scenario_id}" +
")" ")"
) )
execute(sql)
return True return True
+10 -3
View File
@@ -228,9 +228,10 @@ class Friction(SQLSubModel):
ssi = self._status.scenario_id ssi = self._status.scenario_id
return ( return (
self._begin_strickler._owner_scenario == ssi self._begin_strickler is not None
# or self._end_strickler._owner_scenario == ssi and (self._begin_strickler._owner_scenario == ssi
or super(Friction, self).must_be_saved() # or self._end_strickler._owner_scenario == ssi
or super(Friction, self).must_be_saved())
) )
def _db_save(self, execute, data=None): def _db_save(self, execute, data=None):
@@ -364,3 +365,9 @@ class Friction(SQLSubModel):
# self.end_strickler.medium]) # self.end_strickler.medium])
return minor, medium return minor, medium
def update_rks(self, begin_rk, end_rk):
self._begin_rk = begin_rk
self._end_rk = end_rk
self.modified()
+19
View File
@@ -66,6 +66,11 @@ class PointXYZ(Point):
) )
""") """)
execute(
"CREATE INDEX idx_point " +
"ON geometry_pointXYZ(profile, scenario);"
)
return cls._create_submodel(execute) return cls._create_submodel(execute)
@classmethod @classmethod
@@ -90,6 +95,13 @@ class PointXYZ(Point):
"ADD COLUMN deleted BOOLEAN NOT NULL DEFAULT FALSE" "ADD COLUMN deleted BOOLEAN NOT NULL DEFAULT FALSE"
) )
if major == "0" and minor == "2":
if int(release) < 5:
execute(
"CREATE INDEX idx_point " +
"ON geometry_pointXYZ(profile, scenario);"
)
return cls._update_submodel(execute, version, data) return cls._update_submodel(execute, version, data)
@classmethod @classmethod
@@ -338,6 +350,13 @@ class PointXYZ(Point):
self._z = float(value) self._z = float(value)
self.modified() self.modified()
def shift(self, x, y, z):
self.x += x
self.y += y
self.z += z
self.modified()
def get_coordinate(self): def get_coordinate(self):
return (self._x, self._y, self._z) return (self._x, self._y, self._z)
+3 -9
View File
@@ -24,7 +24,7 @@ from typing import List
from functools import reduce from functools import reduce
from dataclasses import dataclass from dataclasses import dataclass
from tools import timer, flatten from tools import trace, timer, flatten
from shapely import geometry from shapely import geometry
from Model.Tools.PamhyrDB import SQLSubModel from Model.Tools.PamhyrDB import SQLSubModel
@@ -396,7 +396,6 @@ class ProfileXYZ(Profile, SQLSubModel):
for point in self._points: for point in self._points:
p = point.copy() p = point.copy()
print(p)
new_p._points.append(p) new_p._points.append(p)
new_p.modified() new_p.modified()
@@ -1074,16 +1073,10 @@ class ProfileXYZ(Profile, SQLSubModel):
def shift(self, x, y, z): def shift(self, x, y, z):
for p in self._points: for p in self._points:
p.x = p.x + x p.shift(x, y, z)
p.y = p.y + y
p.z = p.z + z
self.modified() self.modified()
def modified(self):
self.tab_up_to_date = False
self.station_up_to_date = False
def add_npoints(self, npoints): def add_npoints(self, npoints):
# add npoints in a profile # add npoints in a profile
for k in range(npoints): for k in range(npoints):
@@ -1105,6 +1098,7 @@ class ProfileXYZ(Profile, SQLSubModel):
rk=self.rk, rk=self.rk,
reach=self.reach, reach=self.reach,
status=self._status) status=self._status)
for i, k in enumerate(self.points): for i, k in enumerate(self.points):
p.insert_point(i, k.copy()) p.insert_point(i, k.copy())
+20 -3
View File
@@ -52,6 +52,11 @@ class Reach(SQLSubModel):
self._guidelines_is_valid = False self._guidelines_is_valid = False
self._guidelines = {} self._guidelines = {}
super(Reach, self).__init__(
id=parent.pamhyr_id,
status=status,
)
@property @property
def pamhyr_id(self): def pamhyr_id(self):
return self._parent.pamhyr_id return self._parent.pamhyr_id
@@ -60,6 +65,10 @@ class Reach(SQLSubModel):
def _pamhyr_id(self): def _pamhyr_id(self):
return self._parent.pamhyr_id return self._parent.pamhyr_id
@_pamhyr_id.setter
def _pamhyr_id(self, id):
return
@classmethod @classmethod
def _db_create(cls, execute): def _db_create(cls, execute):
return cls._create_submodel(execute) return cls._create_submodel(execute)
@@ -103,6 +112,9 @@ class Reach(SQLSubModel):
predicate=lambda obj, data: True, predicate=lambda obj, data: True,
modifier=lambda obj, data: None, modifier=lambda obj, data: None,
data={}): data={}):
if predicate(self, data):
modifier(self, data)
for p in self._profiles: for p in self._profiles:
p._data_traversal(predicate, modifier, data) p._data_traversal(predicate, modifier, data)
@@ -199,13 +211,12 @@ class Reach(SQLSubModel):
else: else:
gi = self.get_global_profil_index(index) gi = self.get_global_profil_index(index)
profile.num = gi profile.num = gi
print(f"gi = {gi}")
self._profiles.insert(gi, profile) self._profiles.insert(gi, profile)
self.modified() self.modified()
def delete(self, indexes): def delete(self, indexes):
"""Delete some elements in profile list """Set some elements as deleted in profile list
Args: Args:
indexes: The list of index to delete indexes: The list of index to delete
@@ -247,7 +258,7 @@ class Reach(SQLSubModel):
self.modified() self.modified()
def delete_profiles(self, profiles): def delete_profiles(self, profiles):
"""Delete some elements in profile list """Set profiles list as deleted
Args: Args:
profiles: The list of profile to delete profiles: The list of profile to delete
@@ -956,3 +967,9 @@ class Reach(SQLSubModel):
break break
return global_index return global_index
def update_rks(self, rks):
for rk, profile in zip(rks, self.profiles):
profile.rk = rk
self.modified()
@@ -183,7 +183,6 @@ class BasicHS(SQLSubModel):
bhs._data = BHSValue._db_load( bhs._data = BHSValue._db_load(
execute, data execute, data
) )
print(f"{bhs_pid} : {deleted}")
if deleted: if deleted:
bhs.set_as_deleted() bhs.set_as_deleted()
@@ -274,6 +274,9 @@ class HydraulicStructure(SQLSubModel):
return hs return hs
def _db_save(self, execute, data=None): def _db_save(self, execute, data=None):
if not self.must_be_saved():
return True
execute( execute(
"DELETE FROM hydraulic_structures " + "DELETE FROM hydraulic_structures " +
f"WHERE pamhyr_id = {self.pamhyr_id} " + f"WHERE pamhyr_id = {self.pamhyr_id} " +
@@ -225,6 +225,8 @@ class Data(SQLSubModel):
return new return new
def _db_save(self, execute, data=None): def _db_save(self, execute, data=None):
if not self.must_be_saved():
return True
ind = data["ind"] ind = data["ind"]
execute( execute(
@@ -396,7 +398,7 @@ class InitialConditions(SQLSubModel):
@reach.setter @reach.setter
def reach(self, new): def reach(self, new):
self._reach = reach self._reach = new
self.modified() self.modified()
@property @property
@@ -586,8 +588,6 @@ class InitialConditions(SQLSubModel):
for data in self._data: for data in self._data:
data_height[data["rk"].rk] = data["height"] data_height[data["rk"].rk] = data["height"]
print(data_height)
incline = self._reach.reach.get_incline_median_mean() incline = self._reach.reach.get_incline_median_mean()
logger.debug(f"incline = {incline}") logger.debug(f"incline = {incline}")
@@ -79,16 +79,12 @@ class InitialConditionsAdisTS(SQLSubModel):
@classmethod @classmethod
def _db_update(cls, execute, version, data=None): def _db_update(cls, execute, version, data=None):
major, minor, release = version.strip().split(".") major, minor, release = version.strip().split(".")
created = False
if major == "0" and int(minor) <= 1:
if int(release) < 6:
cls._db_create(execute)
created = True
if major == "0" and int(minor) < 2: if major == "0" and int(minor) < 2:
if not created: if cls.is_table_exists(execute, "initial_conditions_adists"):
cls._db_update_to_0_2_0(execute, data) cls._db_update_to_0_2_0(execute, data)
else:
cls._db_create(execute)
return True return True
@@ -267,7 +263,7 @@ class InitialConditionsAdisTS(SQLSubModel):
@name.setter @name.setter
def name(self, name): def name(self, name):
self._name = name self._name = name
self._status.modified() self.modified()
@property @property
def pollutant(self): def pollutant(self):
@@ -276,7 +272,7 @@ class InitialConditionsAdisTS(SQLSubModel):
@pollutant.setter @pollutant.setter
def pollutant(self, pollutant): def pollutant(self, pollutant):
self._pollutant = pollutant self._pollutant = pollutant
self._status.modified() self.modified()
@property @property
def concentration(self): def concentration(self):
@@ -285,7 +281,7 @@ class InitialConditionsAdisTS(SQLSubModel):
@concentration.setter @concentration.setter
def concentration(self, concentration): def concentration(self, concentration):
self._concentration = concentration self._concentration = concentration
self._status.modified() self.modified()
@property @property
def eg(self): def eg(self):
@@ -294,7 +290,7 @@ class InitialConditionsAdisTS(SQLSubModel):
@eg.setter @eg.setter
def eg(self, eg): def eg(self, eg):
self._eg = eg self._eg = eg
self._status.modified() self.modified()
@property @property
def em(self): def em(self):
@@ -303,7 +299,7 @@ class InitialConditionsAdisTS(SQLSubModel):
@em.setter @em.setter
def em(self, em): def em(self, em):
self._em = em self._em = em
self._status.modified() self.modified()
@property @property
def ed(self): def ed(self):
@@ -312,7 +308,7 @@ class InitialConditionsAdisTS(SQLSubModel):
@ed.setter @ed.setter
def ed(self, ed): def ed(self, ed):
self._ed = ed self._ed = ed
self._status.modified() self.modified()
@property @property
def enabled(self): def enabled(self):
@@ -321,28 +317,45 @@ class InitialConditionsAdisTS(SQLSubModel):
@enabled.setter @enabled.setter
def enabled(self, enabled): def enabled(self, enabled):
self._enabled = enabled self._enabled = enabled
self._status.modified() self.modified()
def new(self, index): def new(self, index):
n = ICAdisTSSpec(status=self._status) n = ICAdisTSSpec(status=self._status)
self._data.insert(index, n) self._data.insert(index, n)
self._status.modified() self.modified()
return n return n
def delete(self, data): def delete(self, data):
self._data = list( list(
filter( map(
lambda x: x not in data, lambda x: x.set_as_deleted(),
self._data data
) )
) )
self._status.modified() self.modified()
def delete_i(self, indexes): def delete_i(self, indexes):
for ind in indexes: list(
del self._data[ind] map(
self._status.modified() lambda e: e[1].set_as_deleted(),
filter(
lambda e: e[0] in indexes,
enumerate(self._data)
)
)
)
self.modified()
def insert(self, index, data): def insert(self, index, data):
self._data.insert(index, data) if data in self._data:
self._status.modified() self.undelete([data])
else:
self._data.insert(index, data)
self.modified()
def undelete(self, lst):
for x in lst:
x.set_as_not_deleted()
self.modified()
@@ -31,7 +31,7 @@ class ICAdisTSSpec(SQLSubModel):
_sub_classes = [] _sub_classes = []
def __init__(self, id: int = -1, name: str = "", def __init__(self, id: int = -1, name: str = "",
status=None): status=None, owner_scenario=None):
super(ICAdisTSSpec, self).__init__() super(ICAdisTSSpec, self).__init__()
self._status = status self._status = status
@@ -77,16 +77,12 @@ class ICAdisTSSpec(SQLSubModel):
@classmethod @classmethod
def _db_update(cls, execute, version, data=None): def _db_update(cls, execute, version, data=None):
major, minor, release = version.strip().split(".") major, minor, release = version.strip().split(".")
created = False
if major == 0 and minor < 1:
if int(release) < 6:
cls._db_create(execute)
created = True
if major == "0" and int(minor) < 2: if major == "0" and int(minor) < 2:
if not created: if cls.is_table_exists(execute, "initial_conditions_adists_spec"):
cls._db_update_to_0_2_0(execute, data) cls._db_update_to_0_2_0(execute, data)
else:
cls._db_create(execute)
return True return True
@@ -194,7 +190,7 @@ class ICAdisTSSpec(SQLSubModel):
new_spec.rate = rate new_spec.rate = rate
new_spec.enabled = enabled new_spec.enabled = enabled
loaded.add(pid) # loaded.add(pid)
new.append(new_spec) new.append(new_spec)
data["scenario"] = scenario.parent data["scenario"] = scenario.parent
@@ -211,7 +207,7 @@ class ICAdisTSSpec(SQLSubModel):
sql = ( sql = (
"INSERT INTO " + "INSERT INTO " +
"initial_conditions_adists_spec(id, deleted, " + "initial_conditions_adists_spec(pamhyr_id, deleted, " +
"ic_default, name, reach, " + "ic_default, name, reach, " +
"start_rk, end_rk, concentration, eg, em, ed, rate, " + "start_rk, end_rk, concentration, eg, em, ed, rate, " +
"enabled, scenario) " + "enabled, scenario) " +
@@ -147,9 +147,9 @@ class Data(SQLSubModel):
it = iter(v) it = iter(v)
pid = next(it) pid = next(it)
delete = next(it) deleted = next(it)
data0 = bc._types[0](next(it)) data0 = lc._types[0](next(it))
data1 = bc._types[1](next(it)) data1 = lc._types[1](next(it))
owner_scenario = next(it) owner_scenario = next(it)
nd = cls( nd = cls(
@@ -69,14 +69,12 @@ class Data(SQLSubModel):
major, minor, release = version.strip().split(".") major, minor, release = version.strip().split(".")
created = False created = False
if major == "0" and int(minor) <= 1:
if int(release) < 7:
cls._db_create(execute)
created = True
if major == "0" and int(minor) < 2: if major == "0" and int(minor) < 2:
if not created: if cls.is_table_exists(
execute, "lateral_contribution_data_adists"):
cls._db_update_to_0_2_0(execute, data) cls._db_update_to_0_2_0(execute, data)
else:
cls._db_create(execute)
return True return True
@@ -132,11 +130,13 @@ class Data(SQLSubModel):
if scenario is None: if scenario is None:
return new return new
lca = data["lca"]
table = execute( table = execute(
"SELECT pamhyr_id, deleted, data0, data1 " + "SELECT pamhyr_id, deleted, data0, data1, scenario " +
"FROM lateral_contribution_data_adists " + "FROM lateral_contribution_data_adists " +
f"WHERE scenario = {scenario.id} " + f"WHERE scenario = {scenario.id} " +
f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))}) " f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))}) " +
f"AND lca = '{lca.id}'" f"AND lca = '{lca.id}'"
) )
@@ -150,16 +150,16 @@ class Data(SQLSubModel):
data1 = next(it) data1 = next(it)
owner_scenario = next(it) owner_scenario = next(it)
data = cls( lc = cls(
data0, data1, data0, data1,
id=pid, status=status, id=pid, status=status,
owner_scenario=owner_scenario owner_scenario=owner_scenario
) )
if deleted: if deleted:
data.set_as_deleted() lc.set_as_deleted()
loaded.add(pid) loaded.add(pid)
new.append(data) new.append(lc)
data["scenario"] = scenario.parent data["scenario"] = scenario.parent
new += cls._db_load(execute, data) new += cls._db_load(execute, data)
@@ -175,7 +175,7 @@ class Data(SQLSubModel):
execute( execute(
"INSERT INTO " + "INSERT INTO " +
"lateral_contribution_adists(pamhyr_id, deleted," + "lateral_contribution_data_adists(pamhyr_id, deleted," +
"data0, data1, lca, scenario) " + "data0, data1, lca, scenario) " +
"VALUES (" + "VALUES (" +
f"{self.id}, {self._db_format(self.is_deleted())}, " + f"{self.id}, {self._db_format(self.is_deleted())}, " +
@@ -241,15 +241,13 @@ class LateralContributionAdisTS(SQLSubModel):
major, minor, release = version.strip().split(".") major, minor, release = version.strip().split(".")
created = False created = False
if major == "0" and int(minor) <= 1: if major == "0" and int(minor) < 2:
if int(release) < 7: if cls.is_table_exists(execute, "lateral_contribution_adists"):
cls._db_update_to_0_2_0(execute, data)
else:
cls._db_create(execute) cls._db_create(execute)
created = True created = True
if major == "0" and int(minor) < 2:
if not created:
cls._db_update_to_0_2_0(execute, data)
if not created: if not created:
return cls._update_submodel(execute, version, data) return cls._update_submodel(execute, version, data)
@@ -372,16 +370,18 @@ class LateralContributionAdisTS(SQLSubModel):
execute( execute(
"INSERT INTO " + "INSERT INTO " +
"lateral_contribution_adists(id, deleted," + "lateral_contribution_adists(pamhyr_id, deleted, " +
"pollutant, reach, begin_rk, end_rk, scenario) " + "pollutant, reach, begin_rk, end_rk, scenario) " +
"VALUES (" + "VALUES (" +
f"{self.id}, {self._db_format(self.is_deleted())}, " + f"{self.id}, {self._db_format(self.is_deleted())}, " +
f"{self._pollutant}, {self.reach}, " + f"{self._pollutant}, {self.reach}, " +
f"{self._begin_rk}, {self._end_rk}" + f"{self._begin_rk}, {self._end_rk}, " +
f"{self._status.scenario_id}" + f"{self._status.scenario_id}" +
")" ")"
) )
data["lca"] = self
ind = 0 ind = 0
for d in self._data: for d in self._data:
data["ind"] = ind data["ind"] = ind
@@ -406,7 +406,7 @@ class LateralContributionAdisTS(SQLSubModel):
return len( return len(
list( list(
filter( filter(
lambda el: not el.is_deleted(), lambda el: el is not None and not el.is_deleted(),
self._data self._data
) )
) )
@@ -452,7 +452,12 @@ class LateralContributionAdisTS(SQLSubModel):
@property @property
def data(self): def data(self):
return self._data.copy() return list(
filter(
lambda el: el is not None and not el.is_deleted(),
self._data
)
)
@property @property
def begin_rk(self): def begin_rk(self):
@@ -471,3 +476,57 @@ class LateralContributionAdisTS(SQLSubModel):
def end_rk(self, end_rk): def end_rk(self, end_rk):
self._end_rk = end_rk self._end_rk = end_rk
self.modified() self.modified()
@property
def _default_0(self):
return self._types[0](0)
@property
def _default_1(self):
return self._types[1](0.0)
def add(self, index: int):
value = Data(self._default_0, self._default_1, status=self._status)
self._data.insert(index, value)
self.modified()
return value
def insert(self, index: int, data: Data):
self._data.insert(index, data)
self.modified()
def delete_i(self, indexes):
self._data = list(
map(
lambda e: e[1].set_as_deleted(),
filter(
lambda e: e[0] not in indexes,
enumerate(self.data)
)
)
)
self.modified()
def index(self, bc):
self._data.index(bc)
def get_i(self, index: int):
return self._data[index]
def get_range(self, _range):
lst = []
for r in _range:
lst.append(r)
return lst
def _set_i_c_v(self, index: int, column: int, value):
v = self._data[index]
v[column] = self._types[column](value)
self._data[index] = v
self.modified()
def set_i_0(self, index: int, value):
self._set_i_c_v(index, 0, value)
def set_i_1(self, index: int, value):
self._set_i_c_v(index, 1, value)
+4 -8
View File
@@ -95,16 +95,12 @@ class OutputRKAdists(SQLSubModel):
@classmethod @classmethod
def _db_update(cls, execute, version, data=None): def _db_update(cls, execute, version, data=None):
major, minor, release = version.strip().split(".") major, minor, release = version.strip().split(".")
created = False
if major == "0" and int(minor) <= 1:
if int(release) < 7:
cls._db_create(execute)
created = True
if major == "0" and int(minor) < 2: if major == "0" and int(minor) < 2:
if not created: if cls.is_table_exists(execute, "OutputRKAdists"):
cls._db_update_to_0_2_0(execute, data) cls._db_update_to_0_2_0(execute, data)
else:
cls._db_create(execute)
return True return True
@@ -193,7 +189,7 @@ class OutputRKAdists(SQLSubModel):
"output_rk_adists(pamhyr_id, deleted, " + "output_rk_adists(pamhyr_id, deleted, " +
"reach, rk, title, scenario) " + "reach, rk, title, scenario) " +
"VALUES (" + "VALUES (" +
f"{self.id}, {self._db_format(self.is_deleted())}" + f"{self.id}, {self._db_format(self.is_deleted())}, " +
f"{self._reach}, {self._rk}, " + f"{self._reach}, {self._rk}, " +
f"'{self._db_format(self._title)}', " + f"'{self._db_format(self._title)}', " +
f"{self._status.scenario_id}" + f"{self._status.scenario_id}" +
+75 -30
View File
@@ -28,6 +28,9 @@ from tools import (
from Model.Tools.PamhyrDB import SQLSubModel from Model.Tools.PamhyrDB import SQLSubModel
from Model.Except import NotImplementedMethodeError from Model.Except import NotImplementedMethodeError
from Model.Scenario import Scenario from Model.Scenario import Scenario
from Model.BoundaryConditionsAdisTS.BoundaryConditionAdisTS import (
BoundaryConditionAdisTS,
)
logger = logging.getLogger() logger = logging.getLogger()
@@ -101,6 +104,12 @@ class PollutantCharacteristics(SQLSubModel):
self._ac = value self._ac = value
elif key == 8: elif key == 8:
self._bc = value self._bc = value
self.propagate_modified()
def propagate_modified(self):
self.modified()
if hasattr(self, "_pollutant") and self._pollutant:
self._pollutant.modified()
@classmethod @classmethod
def _db_create(cls, execute, ext=""): def _db_create(cls, execute, ext=""):
@@ -129,16 +138,12 @@ class PollutantCharacteristics(SQLSubModel):
@classmethod @classmethod
def _db_update(cls, execute, version, data=None): def _db_update(cls, execute, version, data=None):
major, minor, release = version.strip().split(".") major, minor, release = version.strip().split(".")
created = False
if major == "0" and int(minor) <= 1:
if int(release) < 7:
cls._db_create(execute)
created = True
if major == "0" and int(minor) < 2: if major == "0" and int(minor) < 2:
if not created: if cls.is_table_exists(execute, "Pollutants_characteristics"):
cls._db_update_to_0_2_0(execute, data) cls._db_update_to_0_2_0(execute, data)
else:
cls._db_create(execute)
return cls._update_submodel(execute, version, data) return cls._update_submodel(execute, version, data)
@@ -253,6 +258,12 @@ class PollutantCharacteristics(SQLSubModel):
if not self.must_be_saved(): if not self.must_be_saved():
return True return True
execute(
"DELETE FROM pollutants_characteristics " +
f"WHERE pamhyr_id = {self.id} " +
f"AND scenario = {self._status.scenario_id}"
)
execute( execute(
"INSERT INTO " + "INSERT INTO " +
"pollutants_characteristics(pamhyr_id, deleted, " + "pollutants_characteristics(pamhyr_id, deleted, " +
@@ -275,7 +286,10 @@ class PollutantCharacteristics(SQLSubModel):
class Pollutants(SQLSubModel): class Pollutants(SQLSubModel):
_sub_classes = [PollutantCharacteristics] _sub_classes = [
PollutantCharacteristics,
# BoundaryConditionAdisTS
]
def __init__(self, id: int = -1, name: str = "", def __init__(self, id: int = -1, name: str = "",
status=None, owner_scenario=-1): status=None, owner_scenario=-1):
@@ -293,6 +307,11 @@ class Pollutants(SQLSubModel):
self._enabled = True self._enabled = True
self._data = [] self._data = []
self._boundary_conditions_adists = []
@property
def boundary_conditions_adists(self):
return self._boundary_conditions_adists
@property @property
def name(self): def name(self):
@@ -301,7 +320,7 @@ class Pollutants(SQLSubModel):
@name.setter @name.setter
def name(self, name): def name(self, name):
self._name = name self._name = name
self._status.modified() self.modified()
@property @property
def data(self): def data(self):
@@ -309,19 +328,21 @@ class Pollutants(SQLSubModel):
@data.setter @data.setter
def data(self, data): def data(self, data):
self._data = [PollutantCharacteristics(type=int(data[0]), self._data = [
diametre=float(data[1]), PollutantCharacteristics(
rho=float(data[2]), type=int(data[0]),
porosity=float(data[3]), diametre=float(data[1]),
cdc_riv=float(data[4]), rho=float(data[2]),
cdc_cas=float(data[5]), porosity=float(data[3]),
apd=float(data[6]), cdc_riv=float(data[4]),
ac=float(data[7]), cdc_cas=float(data[5]),
bc=float(data[8]), apd=float(data[6]),
pollutant=self, ac=float(data[7]),
status=self._status bc=float(data[8]),
) pollutant=self,
] status=self._status
)
]
@classmethod @classmethod
def _db_create(cls, execute, ext=""): def _db_create(cls, execute, ext=""):
@@ -329,6 +350,7 @@ class Pollutants(SQLSubModel):
CREATE TABLE pollutants{ext}( CREATE TABLE pollutants{ext}(
{cls.create_db_add_pamhyr_id()}, {cls.create_db_add_pamhyr_id()},
deleted BOOLEAN NOT NULL DEFAULT FALSE, deleted BOOLEAN NOT NULL DEFAULT FALSE,
enabled BOOLEAN NOT NULL DEFAULT TRUE,
name TEXT NOT NULL, name TEXT NOT NULL,
{Scenario.create_db_add_scenario()}, {Scenario.create_db_add_scenario()},
{Scenario.create_db_add_scenario_fk()} {Scenario.create_db_add_scenario_fk()}
@@ -345,14 +367,20 @@ class Pollutants(SQLSubModel):
major, minor, release = version.strip().split(".") major, minor, release = version.strip().split(".")
created = False created = False
if major == "0" and int(minor) <= 1: if major == "0" and int(minor) < 2:
if int(release) < 7: if cls.is_table_exists(execute, "Pollutants"):
cls._db_update_to_0_2_0(execute, data)
else:
cls._db_create(execute) cls._db_create(execute)
created = True created = True
if major == "0" and int(minor) < 2: if major == "0" and minor == "2":
if not created: if int(release) < 6:
cls._db_update_to_0_2_0(execute, data) execute(f"ALTER TABLE pollutants " +
f"ADD COLUMN enabled BOOLEAN NOT NULL DEFAULT TRUE")
# cls._db_update_to_0_2_5(execute, data)
# # created = True
if not created: if not created:
return cls._update_submodel(execute, version, data) return cls._update_submodel(execute, version, data)
@@ -391,7 +419,8 @@ class Pollutants(SQLSubModel):
return new return new
table = execute( table = execute(
"SELECT pamhyr_id, deleted, name FROM pollutants " + "SELECT pamhyr_id, deleted, enabled, name, scenario " +
"FROM pollutants " +
f"WHERE scenario = {scenario.id} " + f"WHERE scenario = {scenario.id} " +
f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))})" f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))})"
) )
@@ -402,20 +431,32 @@ class Pollutants(SQLSubModel):
pid = next(it) pid = next(it)
deleted = (next(it) == 1) deleted = (next(it) == 1)
enabled = (next(it) == 1)
name = next(it) name = next(it)
owner_scenario = next(it)
new_pollutant = cls( new_pollutant = cls(
id=pid, name=name, id=pid, name=name,
status=status status=status,
owner_scenario=owner_scenario
) )
if deleted: if deleted:
new_pollutant.set_as_deleted() new_pollutant.set_as_deleted()
if not enabled:
new_pollutant.enabled = False
data["pollutant"] = new_pollutant data["pollutant"] = new_pollutant
new_pollutant._data = PollutantCharacteristics._db_load( new_pollutant._data = PollutantCharacteristics._db_load(
execute, data=data execute, data=data
) )
new_pollutant._boundary_conditions_adists = (
BoundaryConditionAdisTS._db_load(
execute,
data=data
)
)
loaded.add(pid) loaded.add(pid)
new.append(new_pollutant) new.append(new_pollutant)
@@ -444,9 +485,10 @@ class Pollutants(SQLSubModel):
execute( execute(
"INSERT INTO " + "INSERT INTO " +
"pollutants(pamhyr_id, deleted, name, scenario) " + "pollutants(pamhyr_id, deleted, enabled, name, scenario) " +
"VALUES (" + "VALUES (" +
f"{self.id}, {self._db_format(self.is_deleted())}, " + f"{self.id}, {self._db_format(self.is_deleted())}, " +
f"{self._db_format(self._enabled)}, " +
f"'{self._db_format(self._name)}', " + f"'{self._db_format(self._name)}', " +
f"{self._status.scenario_id}" + f"{self._status.scenario_id}" +
")" ")"
@@ -455,6 +497,9 @@ class Pollutants(SQLSubModel):
for d in self._data: for d in self._data:
ok &= d._db_save(execute, data) ok &= d._db_save(execute, data)
for bc in self._boundary_conditions_adists:
ok &= bc._db_save(execute, data)
return ok return ok
def _data_traversal(self, def _data_traversal(self,
+1 -1
View File
@@ -19,7 +19,7 @@
from tools import trace, timer from tools import trace, timer
from Model.Except import NotImplementedMethodeError from Model.Except import NotImplementedMethodeError
from Model.Tools.PamhyrList import PamhyrModelList from Model.Tools.PamhyrListExt import PamhyrModelList
from Model.Pollutants.Pollutants import Pollutants from Model.Pollutants.Pollutants import Pollutants
+1 -3
View File
@@ -263,8 +263,6 @@ class Reservoir(SQLSubModel):
cls._db_update_to_0_2_0_set_node_pid(execute, table, nodes) cls._db_update_to_0_2_0_set_node_pid(execute, table, nodes)
Scenario.update_db_add_scenario(execute, table) Scenario.update_db_add_scenario(execute, table)
print(execute(f"SELECT * FROM {table}"))
cls._db_create(execute, ext="_tmp") cls._db_create(execute, ext="_tmp")
execute( execute(
f"INSERT INTO {table}_tmp " + f"INSERT INTO {table}_tmp " +
@@ -314,7 +312,7 @@ class Reservoir(SQLSubModel):
new_reservoir._node = next( new_reservoir._node = next(
filter( filter(
lambda n: n.id == node_id, data["nodes"] lambda n: n.id == node_id, data["nodes"]
) ), None
) )
data["reservoir"] = new_reservoir data["reservoir"] = new_reservoir
+12 -1
View File
@@ -219,6 +219,11 @@ class Results(SQLSubModel):
qlog=None, qlog=None,
) )
def bufferize(self, key):
if self.is_valid:
for reach in self._river._reachs:
reach.bufferize(self._meta_data["timestamps"], key)
def timestamps_to_struct(self): def timestamps_to_struct(self):
ts = self._meta_data["timestamps"] ts = self._meta_data["timestamps"]
sf = ">" + ''.join(itertools.repeat("d", len(ts))) sf = ">" + ''.join(itertools.repeat("d", len(ts)))
@@ -344,9 +349,15 @@ class Results(SQLSubModel):
f"AND result = {pid}" f"AND result = {pid}"
) )
def _is_valid(self):
return "timestamps" in self._meta_data
def _db_save(self, execute, data=None): def _db_save(self, execute, data=None):
if self._status.scenario.id != self._owner_scenario: if self._status.scenario.id != self._owner_scenario:
return return True
if not self._is_valid():
return False
pid = self._pamhyr_id pid = self._pamhyr_id
if self._solver is None: if self._solver is None:
+5 -1
View File
@@ -21,12 +21,13 @@ import os
from copy import deepcopy from copy import deepcopy
from datetime import datetime from datetime import datetime
from Model.Tools.PamhyrDB import SQLSubModel
from Model.Results.River.RiverAdisTS import River from Model.Results.River.RiverAdisTS import River
logger = logging.getLogger() logger = logging.getLogger()
class Results(object): class Results(SQLSubModel):
def __init__(self, study=None, solver=None, def __init__(self, study=None, solver=None,
repertory="", name="0", type_pol=None): repertory="", name="0", type_pol=None):
self._study = study self._study = study
@@ -90,3 +91,6 @@ class Results(object):
self._repertory, self._repertory,
qlog=None, qlog=None,
) )
def _db_save(self, execute, data=None):
return False
+20 -2
View File
@@ -17,6 +17,7 @@
import struct import struct
import logging import logging
import itertools import itertools
import numpy as np
from tools import flatten from tools import flatten
from functools import reduce from functools import reduce
@@ -76,9 +77,12 @@ class Profile(SQLSubModel):
return self._data[timestamp][key] return self._data[timestamp][key]
return None return None
def has_sediment(self): def has_sediment_layers(self):
return any(map(lambda ts: "sl" in self._data[ts], self._data)) return any(map(lambda ts: "sl" in self._data[ts], self._data))
def has_bedload(self):
return any(map(lambda ts: "zfd" in self._data[ts], self._data))
@classmethod @classmethod
def _db_create(cls, execute, ext=""): def _db_create(cls, execute, ext=""):
execute(f""" execute(f"""
@@ -275,6 +279,7 @@ class Reach(SQLSubModel):
lambda p: p.name[0:8] != 'interpol', self._profiles lambda p: p.name[0:8] != 'interpol', self._profiles
) )
) )
self._buffers = {}
def __len__(self): def __len__(self):
return len(self._profiles) return len(self._profiles)
@@ -295,6 +300,10 @@ class Reach(SQLSubModel):
def profile_mask(self): def profile_mask(self):
return self._profile_mask return self._profile_mask
@property
def buffers(self):
return self._buffers
def profile(self, id): def profile(self, id):
return self._profiles[id] return self._profiles[id]
@@ -302,7 +311,16 @@ class Reach(SQLSubModel):
self._profiles[profile_id].set(timestamp, key, data) self._profiles[profile_id].set(timestamp, key, data)
def has_sediment(self): def has_sediment(self):
return any(map(lambda profile: profile.has_sediment(), self._profiles)) return any(map(lambda profile: profile.has_sediment_layers(),
self._profiles))
def has_bedload(self):
return any(map(lambda profile: profile.has_bedload(), self._profiles))
def bufferize(self, timestamps, key):
self._buffers[key] = np.zeros((len(timestamps), len(self)))
for i, p in enumerate(self._profiles):
self._buffers[key][:, i] = p.get_key(key)
@classmethod @classmethod
def _db_create(cls, execute, ext=""): def _db_create(cls, execute, ext=""):
+9 -3
View File
@@ -180,6 +180,9 @@ class RiverNode(Node):
return nodes return nodes
def _db_save(self, execute, data=None): def _db_save(self, execute, data=None):
if not self.must_be_saved():
return True
execute( execute(
"INSERT OR REPLACE INTO river_node(" + "INSERT OR REPLACE INTO river_node(" +
"pamhyr_id, deleted, name, x, y, scenario" + "pamhyr_id, deleted, name, x, y, scenario" +
@@ -361,7 +364,7 @@ class RiverReach(Edge):
) )
new.enable(enable=enabled) new.enable(enable=enabled)
if deleted: if deleted:
nd.set_as_deleted() new.set_as_deleted()
data["reach"] = new data["reach"] = new
new._reach = Reach._db_load(execute, data) new._reach = Reach._db_load(execute, data)
@@ -377,6 +380,9 @@ class RiverReach(Edge):
return reachs return reachs
def _db_save(self, execute, data=None): def _db_save(self, execute, data=None):
if not self.must_be_saved():
return True
execute( execute(
"INSERT OR REPLACE INTO " + "INSERT OR REPLACE INTO " +
"river_reach(" + "river_reach(" +
@@ -386,7 +392,7 @@ class RiverReach(Edge):
"VALUES (" + "VALUES (" +
f"{self.pamhyr_id}, {self._db_format(self.is_deleted())}, " + f"{self.pamhyr_id}, {self._db_format(self.is_deleted())}, " +
f"'{self._db_format(self._name)}', " + f"'{self._db_format(self._name)}', " +
f"{self._db_format(self.is_enable())}," f"{self._db_format(self.is_enable())}," +
f"{self.node1.pamhyr_id}, {self.node2.pamhyr_id}, " + f"{self.node1.pamhyr_id}, {self.node2.pamhyr_id}, " +
f"{self._status.scenario_id}" + f"{self._status.scenario_id}" +
")" ")"
@@ -543,7 +549,7 @@ class River(Graph):
new._nodes = RiverNode._db_load( new._nodes = RiverNode._db_load(
execute, data execute, data
) )
data["nodes"] = new.nodes() data["nodes"] = new._nodes
data['loaded_pid'] = set() data['loaded_pid'] = set()
new._edges = RiverReach._db_load( new._edges = RiverReach._db_load(
+98 -24
View File
@@ -17,16 +17,51 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import os import os
import logging
from tools import logger_exception from tools import logger_exception, timer, flatten
from Model.Tools.PamhyrDB import SQLSubModel from Model.Tools.PamhyrDB import SQLSubModel
logger = logging.getLogger()
class Scenario(SQLSubModel): class Scenario(SQLSubModel):
_id_cnt = 0 _id_cnt = 0
_sub_classes = [] _sub_classes = []
tables_with_deleted_column = [
# Adists
"output_rk_adists",
"boundary_condition_adists", "boundary_condition_data_adists",
"lateral_contribution_adists", "lateral_contribution_data_adists",
"initial_conditions_adists", "initial_conditions_adists_spec",
"d90_adists", "d90_adists_spec",
"dif_adists_spec",
"pollutants", "pollutants_characteristics",
# Hydraulic
"additional_files",
"boundary_condition", "boundary_condition_data",
"lateral_contribution", "lateral_contribution_data",
"friction", "stricklers",
"hydraulic_structures",
"hydraulic_structures_basic",
"initial_conditions",
"sedimentary_layer", "sedimentary_layer_layer",
"reservoir", "reservoir_data",
"rep_lines",
"geometry_pointXYZ", "geometry_profileXYZ",
"river_reach", "river_node",
"geotiff", "reservoir", "reservoir_data",
]
related_tables = tables_with_deleted_column + [
"dif_adists",
"solver_parameter",
"hydraulic_structures_basic_value",
"results", "results_data", "results_add_data",
]
def __init__(self, def __init__(self,
id: int = -1, id: int = -1,
name: str = "", name: str = "",
@@ -171,29 +206,7 @@ class Scenario(SQLSubModel):
def drop_all(self, execute): def drop_all(self, execute):
execute(f"DELETE FROM scenario WHERE id = {self.id}") execute(f"DELETE FROM scenario WHERE id = {self.id}")
tables = [ tables = self.related_tables
# Adists
"output_rk_adists",
"boundary_condition_adists", "boundary_condition_data_adists",
"lateral_contribution_adists", "lateral_contribution_data_adists",
"initial_conditions_adists",
"d90_adists", "dif_adists",
"pollutants", "pollutants_characteristics",
# Hydraulic
"additional_files",
"boundary_condition", "boundary_condition_data",
"lateral_contribution", "lateral_contribution_data",
"friction", "stricklers",
"hydraulic_structures",
"hydraulic_structures_basic", "hydraulic_structures_basic_value",
"initial_conditions",
"sedimentary_layer", "sedimentary_layer_layer",
"reservoir", "reservoir_data",
"rep_lines",
"solver_parameter",
"geometry_pointXYZ", "geometry_profileXYZ",
"river_reach", "river_node",
]
for table in tables: for table in tables:
execute( execute(
@@ -203,6 +216,67 @@ class Scenario(SQLSubModel):
return True return True
def get_parent_branch(self):
def aux(scenario, acc):
if scenario is None:
return acc
return aux(scenario.parent, [scenario.id] + acc)
return aux(self, [])
@timer
def clean_deleted_data(self, execute):
tables = self.tables_with_deleted_column
branch = self.get_parent_branch()
all_ids = []
for table in tables:
if self.parent is None:
# This scenario is the default scenario, so we can
# delete all data marked as deleted
execute(
f"DELETE FROM {table} " +
f"WHERE scenario = {self.id} " +
"AND deleted = TRUE"
)
continue
# Select pamhyr_id for each deleted data in this scenario
# who do not exists into parents scenarios
ids = execute(
f"""
SELECT pamhyr_id FROM {table}
WHERE deleted = TRUE
AND scenario = {self.id}
AND pamhyr_id NOT IN (
SELECT pamhyr_id FROM {table}
WHERE scenario IN
({', '.join(map(str, branch[:-1]))})
)
""", fetch_one=False
)
if ids is None or len(ids) == 0:
continue
ids = flatten(ids)
logger.debug(
f"({self.name}) Drop deleted data into '{table}' : {ids}"
)
execute(
f"DELETE FROM {table} " +
f"WHERE scenario = {self.id} " +
"AND pamhyr_id IN " +
f"({', '.join(map(str, ids))})"
)
all_ids += ids
return all_ids
@property @property
def id(self): def id(self):
return self._id return self._id
+11
View File
@@ -16,6 +16,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from functools import reduce
from tools import logger_exception from tools import logger_exception
from Model.Tools.PamhyrDict import PamhyrModelDict from Model.Tools.PamhyrDict import PamhyrModelDict
@@ -78,3 +80,12 @@ class Scenarios(PamhyrModelDict):
self._dict[key].set_as_deleted() self._dict[key].set_as_deleted()
self._status.modified() self._status.modified()
def is_leaf(self, scenario):
return not reduce(
lambda acc, s: (
acc or (not s.is_deleted() and s.parent is scenario)
),
self._dict.values(),
False
)
+13
View File
@@ -34,6 +34,7 @@ class StudyStatus(object):
self._status = Status.EDIT self._status = Status.EDIT
self._scenario = scenario self._scenario = scenario
self._saved = True self._saved = True
self._loading = False
@property @property
def scenario_id(self): def scenario_id(self):
@@ -86,6 +87,15 @@ class StudyStatus(object):
def is_saved(self): def is_saved(self):
return self._saved return self._saved
def is_loading(self):
return self._loading
def start_loading(self):
self._loading = True
def stop_loading(self):
self._loading = False
def save(self): def save(self):
logger.debug("model status set as saved") logger.debug("model status set as saved")
self._saved = True self._saved = True
@@ -97,6 +107,9 @@ class StudyStatus(object):
self._saved = True self._saved = True
def modified(self): def modified(self):
if self._loading:
return
if self._saved: if self._saved:
self.version += 1 self.version += 1
+81 -11
View File
@@ -24,12 +24,21 @@ from datetime import datetime
from tools import timer, timestamp from tools import timer, timestamp
from functools import reduce from functools import reduce
from Model.Tools.PamhyrListExt import PamhyrModelList
from Model.Tools.PamhyrDict import PamhyrModelDict
from Model.Tools.PamhyrDB import SQLModel from Model.Tools.PamhyrDB import SQLModel
from Model.Scenarios import Scenarios from Model.Scenarios import Scenarios
from Model.Scenario import Scenario from Model.Scenario import Scenario
from Model.Status import StudyStatus from Model.Status import StudyStatus
from Model.Except import NotImplementedMethodeError from Model.Except import NotImplementedMethodeError
from Model.River import River from Model.River import River
from Model.Geometry.Reach import Reach
from Model.HydraulicStructures.HydraulicStructures import (
HydraulicStructure
)
from Model.HydraulicStructures.Basic.HydraulicStructures import (
BasicHS
)
from Checker.Study import * from Checker.Study import *
@@ -37,7 +46,7 @@ logger = logging.getLogger()
class Study(SQLModel): class Study(SQLModel):
_version = "0.2.3" _version = "0.2.6"
_sub_classes = [ _sub_classes = [
Scenario, Scenario,
@@ -449,8 +458,60 @@ class Study(SQLModel):
[self.scenarios, self._river], [self.scenarios, self._river],
data=progress data=progress
) )
# Clear DB for current scenario
ids = self.status.scenario.clean_deleted_data(self.execute)
self.memory_clean(ids)
progress()
self.commit() self.commit()
@timer
def memory_clean(self, ids):
if len(ids) == 0:
return
reach_class = Reach
hs_classes = [HydraulicStructure, BasicHS]
list_classes = set(PamhyrModelList.__subclasses__())
dict_classes = set(PamhyrModelDict.__subclasses__())
def modifier(obj, data):
t = type(obj)
if t is reach_class:
obj._profiles = list(
filter(
lambda el: el.id not in ids,
obj._profiles
)
)
elif t in hs_classes:
obj._data = list(
filter(
lambda el: el.id not in ids,
obj._data
)
)
elif t in dict_classes:
new = {}
for key in obj._dict:
if obj._dict[key].id not in ids:
new[key] = obj._dict[key]
obj._dict = new
elif t in list_classes:
obj._lst = list(
filter(
lambda el: el.id not in ids,
obj._lst
)
)
self.river._data_traversal(
lambda o, d: True, modifier, {}
)
def sql_save_request_count(self, *args, **kargs): def sql_save_request_count(self, *args, **kargs):
return self._count() return self._count()
@@ -459,7 +520,10 @@ class Study(SQLModel):
[self.scenarios, self._river] [self.scenarios, self._river]
) )
logger.debug(cnt) logger.debug(cnt)
return cnt + 7 # HACK: 8 is the number of SQL request do and dont counted in
# submodel save_count (let's count sql execute into the
# '_save' method)
return cnt + 8
def close(self): def close(self):
"""Close db connection """Close db connection
@@ -485,7 +549,8 @@ class Study(SQLModel):
self.status.scenario = scenario self.status.scenario = scenario
if reduce( if reduce(
lambda a, s: a or (s.parent is scenario), lambda a, s: a or (s.parent is scenario
and not s.is_deleted()),
self.scenarios.lst, self.scenarios.lst,
False False
): ):
@@ -511,15 +576,20 @@ class Study(SQLModel):
"scenario": scenario "scenario": scenario
} }
# Load river data self.status.start_loading()
river = River._db_load( try:
sql_exec, data=data # Load river data
) river = River._db_load(
data["study"] = self sql_exec, data=data
river._db_load_results(sql_exec, data=data) )
data["study"] = self
river._db_load_results(sql_exec, data=data)
finally:
self.status.stop_loading()
self.status.scenario = old_scenario
self._river_scenario_cache[scenario] = river # FIXME: Disable scenario cache to save memory usage
self.status.scenario = old_scenario # self._river_scenario_cache[scenario] = river
return river return river
+23 -2
View File
@@ -26,6 +26,8 @@ from functools import reduce
from SQL import SQL from SQL import SQL
from Model.Except import NotImplementedMethodeError from Model.Except import NotImplementedMethodeError
from tools import trace
from Model.Tools.PamhyrID import PamhyrID from Model.Tools.PamhyrID import PamhyrID
logger = logging.getLogger() logger = logging.getLogger()
@@ -48,11 +50,9 @@ class SQLModel(SQL):
self._cur = self._db.cursor() self._cur = self._db.cursor()
if is_new: if is_new:
logger.info("Create database")
self._create() # Create db self._create() # Create db
# self._save() # Save # self._save() # Save
else: else:
logger.info("Update database")
self._update() # Update db scheme if necessary self._update() # Update db scheme if necessary
# self._load() # Load data # self._load() # Load data
@@ -202,6 +202,8 @@ class SQLSubModel(PamhyrID):
""" """
if self._status is None: if self._status is None:
return return
if self._status.is_loading():
return
self._owner_scenario = self._status.scenario_id self._owner_scenario = self._status.scenario_id
self._status.modified() self._status.modified()
@@ -218,6 +220,8 @@ class SQLSubModel(PamhyrID):
""" """
if self._status is None: if self._status is None:
return return
if self._status.is_loading():
return
self._owner_scenario = self._status.scenario_id self._owner_scenario = self._status.scenario_id
self._status.modified() self._status.modified()
@@ -297,6 +301,15 @@ class SQLSubModel(PamhyrID):
""" """
raise NotImplementedMethodeError(cls, cls._db_update) raise NotImplementedMethodeError(cls, cls._db_update)
@classmethod
def is_table_exists(cls, execute, table):
return any(
execute(
"SELECT name FROM sqlite_master " +
f"WHERE type='table' AND name='{table}'"
)
)
@classmethod @classmethod
def _db_update_to_0_2_0_set_node_pid(cls, execute, table, nodes): def _db_update_to_0_2_0_set_node_pid(cls, execute, table, nodes):
els = execute( els = execute(
@@ -311,6 +324,14 @@ class SQLSubModel(PamhyrID):
if node_id == -1: if node_id == -1:
continue continue
if node_id not in nodes:
# ⚠️ cas important : probablement déjà migré
logger.warning(
f"node_id {node_id} not in nodes " +
"→ probably already migrated"
)
continue
execute( execute(
f"UPDATE {table} " + f"UPDATE {table} " +
f"SET node = {nodes[node_id]} " + f"SET node = {nodes[node_id]} " +
+3
View File
@@ -97,6 +97,9 @@ class PamhyrModelDict(SQLSubModel):
Nothing Nothing
""" """
if predicate(self, data):
modifier(self, data)
for key in self._dict: for key in self._dict:
self._dict[key]\ self._dict[key]\
._data_traversal(predicate, modifier, data) ._data_traversal(predicate, modifier, data)
+3
View File
@@ -100,6 +100,9 @@ class PamhyrModelList(SQLSubModel):
Nothing Nothing
""" """
if predicate(self, data):
modifier(self, data)
for el in self._lst: for el in self._lst:
el._data_traversal(predicate, modifier, data) el._data_traversal(predicate, modifier, data)
+4 -1
View File
@@ -107,6 +107,9 @@ class PamhyrModelList(SQLSubModel):
Returns: Returns:
Nothing Nothing
""" """
if predicate(self, data):
modifier(self, data)
for el in self._lst: for el in self._lst:
el._data_traversal(predicate, modifier, data) el._data_traversal(predicate, modifier, data)
@@ -168,7 +171,7 @@ class PamhyrModelList(SQLSubModel):
lambda x: x[1], lambda x: x[1],
filter( filter(
lambda x: x[0] in indexes, lambda x: x[0] in indexes,
enumerate(self._lst) enumerate(self.lst)
) )
) )
) )
+227 -73
View File
@@ -164,13 +164,194 @@ class AdisTS(CommandLineSolver):
return lst return lst
def input_param(self): def input_param(self):
name = self._study.name name = self._study.name.replace(" ", "_")
return f"{name}.REP" return f"{name}.REP"
def log_file(self): def log_file(self):
name = self._study.name name = self._study.name.replace(" ", "_")
return f"{name}.TRA" return f"{name}.TRA"
def _export_ST(self, study, repertory, qlog, name="0"):
files = []
if qlog is not None:
qlog.put("Export ST file")
os.makedirs(os.path.join(repertory, "net"), exist_ok=True)
# Write header
edges = study.river.enable_edges()
for edge in edges:
name = f"Reach_{edge.id + 1:>3}".replace(" ", "0")
with adists_file_open(
os.path.join(repertory, "net", f"{name}.ST"),
"w+"
) as f:
files.append(str(os.path.join("net", f"{name}.ST")))
cnt_num = 1
for profile in edge.reach.profiles:
self._export_ST_profile_header(
f, files, profile, cnt_num
)
cnt_num += 1
# Points
for point in profile.points:
self._export_ST_point_line(
f, files, point
)
# Profile last line
f.write(f" 999.9990 999.9990 999.9990\n")
def _export_ST_profile_header(self, wfile, files,
profile, cnt):
num = f"{cnt:>6}"
c1 = f"{profile.code1:>6}"
c2 = f"{profile.code2:>6}"
t = f"{len(profile.points):>6}"
rk = f"{profile.rk:>12f}"[0:12]
pname = profile.name
if profile.name == "":
# Generate name from profile id prefixed with
# 'p' (and replace space char with '0' char)
pname = f"p{profile.id:>3}".replace(" ", "0")
name = f"{pname:<19}"
# Generate sediment additional data if available
sediment = ""
if profile.sl is not None:
if not any(filter(lambda f: ".GRA" in f, files)):
files.append(self._gra_file)
# Number of layers
nl = len(profile.sl)
sediment = f" {nl:>3}"
# Layers data
for layer in profile.sl.layers:
sediment += (
f" {layer.height:>10} {layer.d50:>10} " +
f"{layer.sigma:>10} " +
f"{layer.critical_constraint:>10}"
)
# Profile header line
wfile.write(f"{num}{c1}{c2}{t} {rk} {pname} {sediment}\n")
def _export_ST_point_line(self, wfile, files, point):
x = f"{point.x:<12.4f}"[0:12]
y = f"{point.y:<12.4f}"[0:12]
z = f"{point.z:<12.4f}"[0:12]
n = f"{point.name:<3}"
# Generate sediment additional data if available
sediment = ""
prev = point.z
if point.sl is not None:
# Number of layers
nl = len(point.sl)
sediment = f"{nl:>3}"
# Layers data
for layer in point.sl.layers:
prev = round(prev - layer.height, 5)
sediment += (
f" {prev:>10} {layer.d50:>10} " +
f"{layer.sigma:>10} " +
f"{layer.critical_constraint:>10}"
)
# Point line
wfile.write(f"{x} {y} {z} {n} {sediment}\n")
def _export_NET(self, study, repertory, qlog=None, name="0"):
if qlog is not None:
qlog.put("Export NET file")
with adists_file_open(
os.path.join(repertory, f"{name}.NET"), "w+") as f:
edges = study.river.enable_edges()
for e in edges:
name = f"Reach_{e.id + 1:>3}".replace(" ", "0")
id = name
n1 = f"{self.get_node_name(e.node1):3}".replace(" ", "x")
n2 = f"{self.get_node_name(e.node2):3}".replace(" ", "x")
file = os.path.join("net", name + ".ST")
f.write(f"{id} {n1} {n2} {file}\n")
def _export_fake_INI(self, study, repertory, qlog=None, name="0"):
if qlog is not None:
qlog.put("Export fake INI file")
with adists_file_open(
os.path.join(
repertory, "Mage_fin.ini"
), "w+"
) as f:
edges = study.river.enable_edges()
for i, edge in enumerate(edges):
lst = list(filter(
lambda f: f.is_full_defined(),
edge.frictions.frictions
))
rk_min = 9999999.9
rk_max = -9999999.9
coeff_min = -1.0
coeff_max = -1.0
for s in lst: # TODO optimise ?
if s.begin_rk > rk_max:
rk_max = s.begin_rk
coeff_max = s.begin_strickler
if s.begin_rk < rk_min:
rk_min = s.begin_rk
coeff_min = s.begin_strickler
if s.end_rk > rk_max:
rk_max = s.end_rk
coeff_max = s.end_strickler
if s.end_rk < rk_min:
rk_min = s.end_rk
coeff_min = s.end_strickler
print("min max", rk_min, rk_max)
def get_stricklers_from_rk(rk, lst):
print("rk", rk)
coeff = None
if rk > rk_max:
coeff = coeff_max
elif rk < rk_min:
coeff = coeff_min
else:
for s in lst:
if (rk >= s.begin_rk and rk <= s.end_rk or
rk <= s.begin_rk and rk >= s.end_rk):
coeff = s.begin_strickler # TODO: inerpolate
break
# TODO interpolation if rk is not in frictons
if coeff is None:
logger.error(
"Study frictions are not fully defined"
)
return None
return coeff.minor, coeff.medium
for j, profile in enumerate(edge.reach.profiles):
coef_min, coef_moy = get_stricklers_from_rk(profile.rk,
lst)
f.write(
f" {i+1:3}{j+1:4}{' '*116}{coef_min:9}{coef_moy:9}\n")
def _export_REP_additional_lines(self, study, rep_file): def _export_REP_additional_lines(self, study, rep_file):
lines = filter( lines = filter(
lambda line: line.is_enabled(), lambda line: line.is_enabled(),
@@ -192,7 +373,7 @@ class AdisTS(CommandLineSolver):
), "w+" ), "w+"
) as f: ) as f:
path = os.path.join("..", mage_rep, name) path = os.path.join("..", mage_rep, name)
f.write(f"NET {path}.NET\n") f.write(f"NET {name}.NET\n")
f.write(f"REP {path}.REP\n") f.write(f"REP {path}.REP\n")
for file in files: for file in files:
@@ -201,13 +382,25 @@ class AdisTS(CommandLineSolver):
self._export_REP_additional_lines(study, f) self._export_REP_additional_lines(study, f)
path_mage_net = os.path.join(os.path.abspath( self._export_ST(study, repertory, qlog, name=name)
os.path.join(repertory, os.pardir) self._export_NET(study, repertory, qlog, name=name)
), os.path.join(mage_rep, "net"))
path_adists_net = os.path.join(repertory, "net")
if os.path.exists(path_mage_net): # fake mage_fin.ini:
shutil.copytree(path_mage_net, path_adists_net, dirs_exist_ok=True)
path_mage = os.path.join(os.path.abspath(
os.path.join(repertory, os.pardir)), mage_rep)
self._export_fake_INI(study, path_mage,
qlog, name=name)
# path_mage_net = os.path.join(os.path.abspath(
# os.path.join(repertory, os.pardir)
# ), os.path.join(mage_rep, "net"))
# path_adists_net = os.path.join(repertory, "net")
# if os.path.exists(path_mage_net):
# shutil.copytree(path_mage_net,
# path_adists_net,
# dirs_exist_ok=True)
@timer @timer
def export(self, study, repertory, qlog=None): def export(self, study, repertory, qlog=None):
@@ -262,47 +455,6 @@ class AdisTS(CommandLineSolver):
def get_reach_name(self, reach): def get_reach_name(self, reach):
return f"Reach_{reach.pamhyr_id:>3}".replace(" ", "0") return f"Reach_{reach.pamhyr_id:>3}".replace(" ", "0")
def get_node_name(self, node):
"""Generate a 3 char name for node
Args:
node: The node
Returns:
A 3 char name string
"""
n = node.pamhyr_id
# print("node name id: ", n)
# if n in self._nodes_names:
# return self._nodes_names[n]
name = ""
checked_new = False
while not checked_new:
self._nodes_cnt += 1
nc = self._nodes_cnt
name = "".join(
map(
lambda i: self._alph[i % self._l_alph],
[
int(nc / (self._l_alph * self._l_alph)),
int(nc / self._l_alph),
nc
]
)
)
checked_new = name not in self._nodes_views
self._nodes_views.add(name)
self._nodes_names[n] = name
return name
################################# #################################
# Adis-TS in weak coupling mode # # Adis-TS in weak coupling mode #
################################# #################################
@@ -749,29 +901,29 @@ class AdisTSwc(AdisTS):
) - k) ) - k)
data = np.fromfile(f, dtype=np.int32, count=1) # (end) data = np.fromfile(f, dtype=np.int32, count=1) # (end)
# fifth line (useless) # fifth line (useless)
data = np.fromfile(f, dtype=np.int32, count=1) # (start)
zmin_OLD = np.fromfile(f, dtype=np.float32, count=1)[0]
data = np.fromfile(f, dtype=np.int32, count=1) # (end)
# sixth line
zf = np.zeros(ismax, dtype=np.float32)
z = np.zeros(ismax * 3, dtype=np.float32)
for k in range(0, ismax, kbl):
data = np.fromfile(f, dtype=np.int32, count=1) # (start) data = np.fromfile(f, dtype=np.int32, count=1) # (start)
zmin_OLD = np.fromfile(f, dtype=np.float32, count=1)[0] z[3 * k:3 * min(k + kbl, ismax)] = \
np.fromfile(f,
dtype=np.float32,
count=3 * (min(k + kbl, ismax) - k)
)
# z[i*3+1] and z[i*3+2] are useless
data = np.fromfile(f, dtype=np.int32, count=1) # (end) data = np.fromfile(f, dtype=np.int32, count=1) # (end)
# sixth line zf = [z[i * 3] for i in range(ismax)]
zf = np.zeros(ismax, dtype=np.float32) # seventh line (useless)
z = np.zeros(ismax * 3, dtype=np.float32) for k in range(0, ismax, kbl):
for k in range(0, ismax, kbl): data = np.fromfile(f, dtype=np.int32, count=1) # (start)
data = np.fromfile(f, dtype=np.int32, count=1) # (start) zero = np.fromfile(f, dtype=np.int32, count=ismax)
z[3 * k:3 * min(k + kbl, ismax)] = \ data = np.fromfile(f, dtype=np.int32, count=1) # (end)
np.fromfile(f, # end header
dtype=np.float32,
count=3 * (min(k + kbl, ismax) - k)
)
# z[i*3+1] and z[i*3+2] are useless
data = np.fromfile(f, dtype=np.int32, count=1) # (end)
zf = [z[i * 3] for i in range(ismax)]
# seventh line (useless)
for k in range(0, ismax, kbl):
data = np.fromfile(f, dtype=np.int32, count=1) # (start)
zero = np.fromfile(f, dtype=np.int32, count=ismax)
data = np.fromfile(f, dtype=np.int32, count=1) # (end)
# end header
def ip_to_r(i): def ip_to_r(i):
return iprofiles[ return iprofiles[
@@ -793,6 +945,7 @@ class AdisTSwc(AdisTS):
for file_bin in path_files: for file_bin in path_files:
key_pol = os.path.basename(file_bin)[0:-4] key_pol = os.path.basename(file_bin)[0:-4]
data_tmp[key_pol] = {} data_tmp[key_pol] = {}
logger.info(f"read_bin: Start reading '{file_bin}' ...")
with open(file_bin, 'rb') as f: with open(file_bin, 'rb') as f:
# header # header
# first line # first line
@@ -844,7 +997,9 @@ class AdisTSwc(AdisTS):
# seventh line (useless) # seventh line (useless)
for k in range(0, ismax, kbl): for k in range(0, ismax, kbl):
data = np.fromfile(f, dtype=np.int32, count=1) # (start) data = np.fromfile(f, dtype=np.int32, count=1) # (start)
zero = np.fromfile(f, dtype=np.int32, count=ismax) zero = np.fromfile(
f, dtype=np.int32, count=min(k + kbl, ismax) - k
)
data = np.fromfile(f, dtype=np.int32, count=1) # (end) data = np.fromfile(f, dtype=np.int32, count=1) # (end)
# end header # end header
# data # data
@@ -875,7 +1030,6 @@ class AdisTSwc(AdisTS):
for r, reach in enumerate(reachs): for r, reach in enumerate(reachs):
for i in range(is1[r]-1, is2[r]): for i in range(is1[r]-1, is2[r]):
p_i = ip_to_ri(reach, i) p_i = ip_to_ri(reach, i)
for t_data in timestamps_keys: for t_data in timestamps_keys:
pol_view = [] pol_view = []
for pol in pollutants_keys: for pol in pollutants_keys:
+67 -26
View File
@@ -19,6 +19,8 @@
import os import os
import logging import logging
import numpy as np import numpy as np
import time
from functools import reduce
from itertools import chain from itertools import chain
from tools import timer, trace, logger_exception from tools import timer, trace, logger_exception
@@ -325,7 +327,7 @@ class Mage(CommandLineSolver):
v0 = d[0] v0 = d[0]
v1 = d[1] v1 = d[1]
if t in ["HYD", "QSO", "LIM"]: if t in ["HYD", "LIM"]:
v0 /= 60 # Convert first column to minute v0 /= 60 # Convert first column to minute
f.write(f"{v0:9} {v1:9} \n") f.write(f"{v0:9} {v1:9} \n")
@@ -384,28 +386,28 @@ class Mage(CommandLineSolver):
return files return files
def _export_LC(self, study, lateral, f, qlog, name="0"): def _export_LC(self, study, lateral, f, qlog, name="0"):
if lateral.edge is None: if lateral.reach is None:
return return
if lateral.is_deleted(): if lateral.is_deleted():
return return
edges = study.river.enable_edges() edges = study.river.enable_edges()
if lateral.edge not in edges: if lateral.reach not in edges:
return return
eid, _ = next( eid, _ = next(
filter( filter(
lambda e: e[1] == lateral.edge, lambda e: e[1] == lateral.reach,
enumerate(edges) enumerate(edges)
) )
) )
name = f"{eid+1:>3}" name = f"{eid+1:>3}"
# name = f"Reach_{lateral.edge.id + 1:>3}".replace(" ", "0") # name = f"Reach_{lateral.reach.id + 1:>3}".replace(" ", "0")
f.write(f"* {lateral.edge.name} ({name}) {lateral.lctype}\n") f.write(f"* {lateral.reach.name} ({name}) {lateral.lctype}\n")
f.write( f.write(
f"${name} " + f"${name} " +
f"{lateral.begin_rk:>10.4f} {lateral.end_rk:>10.4f}\n" f"{lateral.begin_rk:>10.4f} {lateral.end_rk:>10.4f}\n"
) )
header = lateral.header header = lateral.header
@@ -544,6 +546,15 @@ class Mage(CommandLineSolver):
return files return files
def skip_hs(self, hs):
return (
hs.input_reach is None
or not hs.input_reach.is_enable()
or not hs.enabled
or hs.input_section is None
or hs.is_deleted()
)
def _export_SIN(self, study, repertory, qlog, name="0"): def _export_SIN(self, study, repertory, qlog, name="0"):
files = [] files = []
@@ -569,19 +580,7 @@ class Mage(CommandLineSolver):
files.append(f"{name}.SIN") files.append(f"{name}.SIN")
for hs in hydraulic_structures: for hs in hydraulic_structures:
if hs.input_reach is None: if self.skip_hs(hs):
continue
if not hs.input_reach.is_enable():
continue
if not hs.enabled:
continue
if hs.input_section is None:
continue
if hs.is_deleted():
continue continue
f.write( f.write(
@@ -988,7 +987,7 @@ class Mage8(Mage):
# Data # Data
for d in bound.data: for d in bound.data:
f.write(f"{d[0]:10.3f}{d[1]:10.3f}\n") f.write(f"{d[0]/60:10.3f}{d[1]:10.3f}\n")
return files return files
@@ -1035,6 +1034,7 @@ class Mage8(Mage):
@timer @timer
def read_bin(self, study, fname, results, qlog=None, name="0"): def read_bin(self, study, fname, results, qlog=None, name="0"):
logger.info(f"read_bin: Start reading '{fname}' ...") logger.info(f"read_bin: Start reading '{fname}' ...")
start = time.time()
with mage_file_open(fname, "r") as f: with mage_file_open(fname, "r") as f:
def newline(): return np.fromfile(f, dtype=np.int32, count=1) def newline(): return np.fromfile(f, dtype=np.int32, count=1)
@@ -1117,7 +1117,8 @@ class Mage8(Mage):
# Z and Y (4th line) # Z and Y (4th line)
newline() newline()
_ = read_float(3*nb_profile) tmp = read_float(3*nb_profile)
zfd = tmp[::3]
endline() endline()
# Data # Data
@@ -1180,10 +1181,9 @@ class Mage8(Mage):
logger.info(f"compute tab...") logger.info(f"compute tab...")
for r in reachs: for r in reachs:
for p in r.profiles: for p in r.profiles:
if not p.geometry.tab_up_to_date: p.geometry.compute_tabulation()
p.geometry.compute_tabulation()
logger.info(f"compute velocily...") logger.info(f"compute velocity...")
for r in reachs: for r in reachs:
for t in ts_list: for t in ts_list:
@@ -1195,6 +1195,11 @@ class Mage8(Mage):
r.set(i, t, "V", v) r.set(i, t, "V", v)
logger.info(f"read_bin: ... end with {len(ts)} timestamp read") logger.info(f"read_bin: ... end with {len(ts)} timestamp read")
results.bufferize("Z")
results.bufferize("Q")
results.bufferize("V")
logger.info(f"reading time: '{time.time() - start}'")
@timer @timer
def read_gra(self, study, repertory, results, qlog=None, name="0"): def read_gra(self, study, repertory, results, qlog=None, name="0"):
if not study.river.has_sediment(): if not study.river.has_sediment():
@@ -1202,6 +1207,7 @@ class Mage8(Mage):
fname = os.path.join(repertory, f"{name}.GRA") fname = os.path.join(repertory, f"{name}.GRA")
logger.info(f"read_gra: Start reading '{fname}' ...") logger.info(f"read_gra: Start reading '{fname}' ...")
start = time.time()
with mage_file_open(fname, "r") as f: with mage_file_open(fname, "r") as f:
def newline(): return np.fromfile(f, dtype=np.int32, count=1) def newline(): return np.fromfile(f, dtype=np.int32, count=1)
@@ -1332,7 +1338,7 @@ class Mage8(Mage):
i_pts = 0 i_pts = 0
i_data = 0 i_data = 0
# Loop on cross section # Loop on cross sections
for i in range(n): for i in range(n):
sec_sl = [] sec_sl = []
reach = ip_to_r(i) reach = ip_to_r(i)
@@ -1361,8 +1367,43 @@ class Mage8(Mage):
) )
end = newline().size <= 0 end = newline().size <= 0
ts_list = sorted(ts)
logger.info(f"compute river bed elevation...")
for r in reachs:
z_min = r.geometry.get_z_min()
sls = list(map(
lambda p: p.get_ts_key(ts_list[0], "sl")[0],
r.profiles
))
z_br = list(map(
lambda z, sl: reduce(
lambda z, h: z - h[0],
sl, z
),
z_min, # Original geometry
sls # Original sediment layers
))
for t in ts_list:
sls = list(map(
lambda p: p.get_ts_key(t, "sl")[0],
r.profiles
))
zfd = list(map(
lambda z, sl: reduce(
lambda z, h: z + h[0],
sl, z
),
z_br, # bedrock
sls # Original sediment layers
))
for i, p in enumerate(r.profiles):
r.set(i, t, "zfd", zfd[i])
results.set("sediment_timestamps", ts) results.set("sediment_timestamps", ts)
logger.info(f"read_gra: ... end with {len(ts)} timestamp read") logger.info(f"read_gra: ... end with {len(ts)} timestamp read")
logger.debug(f"reading time: '{time.time() - start}'")
@timer @timer
def results(self, study, repertory, def results(self, study, repertory,
+122 -6
View File
@@ -58,12 +58,12 @@ class Rubar3(CommandLineSolver):
("rubarbe_iovis", "n"), ("rubarbe_iovis", "n"),
("rubarbe_rep", "n"), ("rubarbe_rep", "n"),
("rubarbe_tinit", "000:00:00:00"), ("rubarbe_tinit", "000:00:00:00"),
("rubarbe_tmax", "999:99:99:00"), ("rubarbe_tmax", "000:01:00:00"), # 1 day
("rubarbe_tiopdt", "000:00:00:00"), ("rubarbe_tiopdt", "000:00:00:00"),
("rubarbe_dt", "5.0"), ("rubarbe_dt", "5.0"),
("rubarbe_ts", "999:99:99:00"), ("rubarbe_ts", "999:99:99:00"),
("rubarbe_dtsauv", "00:00:00:05"), ("rubarbe_dtsauv", "00:00:05:00"),
("rubarbe_psave", "00:00:00:05"), ("rubarbe_psave", "00:00:05:00"),
("rubarbe_fdeb1", "1"), ("rubarbe_fdeb1", "1"),
("rubarbe_fdeb2", "10"), ("rubarbe_fdeb2", "10"),
("rubarbe_fdeb3", "100"), ("rubarbe_fdeb3", "100"),
@@ -426,7 +426,6 @@ class Rubar3(CommandLineSolver):
coeff = coeff_min coeff = coeff_min
else: else:
for s in lst: for s in lst:
print(s.begin_rk, s.end_rk)
if (rk >= s.begin_rk and rk <= s.end_rk or if (rk >= s.begin_rk and rk <= s.end_rk or
rk <= s.begin_rk and rk >= s.end_rk): rk <= s.begin_rk and rk >= s.end_rk):
coeff = s.begin_strickler # TODO: inerpolate coeff = s.begin_strickler # TODO: inerpolate
@@ -473,7 +472,6 @@ class Rubar3(CommandLineSolver):
last = profiles[-1] last = profiles[-1]
if first not in data or last not in data: if first not in data or last not in data:
print(data)
logger.error( logger.error(
"Study initial condition is not fully defined" "Study initial condition is not fully defined"
) )
@@ -590,6 +588,8 @@ class Rubar3(CommandLineSolver):
# Add results reach to reach list # Add results reach to reach list
res_reach = results.river.add(i) res_reach = results.river.add(i)
reachs.append((res_reach, len(reach) + 1)) reachs.append((res_reach, len(reach) + 1))
results.set("nb_reach", f"{len(reachs)}")
results.set("nb_profile", f"{sum([r[1] - 1 for r in reachs])}")
def read_data_line(f): def read_data_line(f):
line = f.readline().split() line = f.readline().split()
@@ -610,6 +610,28 @@ class Rubar3(CommandLineSolver):
) )
ts = set() ts = set()
timestamp = next(filter(
lambda p: p.name == 'rubarbe_tinit',
study.river.get_params(self._type).parameters
)).value
if timestamp.count(':') == 3:
timestamp = old_pamhyr_date_to_timestamp(timestamp)
ts.add(timestamp)
# add initial condition
for r, edge in enumerate(study.river.enable_edges()):
reach = edge.reach
ics = study.river.initial_conditions.get(edge)
q = ics.get_discharge()
z = ics.get_elevation()
k = 0
for i, j in zip(z, q):
v = reach.profiles[k].speed(i, j)
set_and_compute_limites(reachs[r][0], k, i, j, v)
k += 1
# start read
end = False end = False
while True: while True:
line = f.readline() line = f.readline()
@@ -650,13 +672,107 @@ class Rubar3(CommandLineSolver):
set_and_compute_limites(reach, ind, z+h, q, s) set_and_compute_limites(reach, ind, z+h, q, s)
@timer @timer
def results(self, study, repertory, qlog=None, name="0"): def write_bin(self, study, fname, results, qlog=None, name="0"):
logger.info(f"write_bin: Start writing '{fname}' ...")
with open(fname, "wb") as f:
def newline(j): return np.asarray([j], dtype=np.int32).tofile(f)
def endline(j): return np.asarray([j], dtype=np.int32).tofile(f)
def write_int(i):
newline(len(i)*4)
np.array(i, dtype=np.int32).tofile(f)
endline(len(i)*4)
def write_float(i):
newline(len(i)*4)
np.array(i, dtype=np.float32).tofile(f)
endline(len(i)*4)
def write_float64(i):
newline(len(i)*8)
np.array(i, dtype=np.float64).tofile(f)
endline(len(i)*8)
def write_float64(i):
newline(len(i)*8)
np.array(i, dtype=np.float64).tofile(f)
endline(len(i)*8)
def write_data(npts, t, a, val):
newline(npts*4 + 13)
np.array(npts, dtype=np.int32).tofile(f)
np.array(t, dtype=np.float64).tofile(f)
np.array(bytearray(a.encode()), dtype=np.byte).tofile(f)
np.array(val, dtype=np.float32).tofile(f)
endline(npts*4 + 13)
ts_list = sorted(results.get("timestamps"))
profiles = []
for r in results.river.reachs:
profiles += r.profiles
# Meta data (1st line)
nb_reach = len(results.river)
nb_profile = len(profiles)
write_int([nb_reach, nb_profile, "82"])
# Reach information (2nd line)
is1 = []
is2 = []
ltmp = []
i = 1
for r in results.river.reachs:
j = i+len(r)-1
ltmp += [i, j]
is1.append(i)
is2.append(j)
i += i+len(r)
write_int(ltmp)
# X (3rd line)
rk = []
for r in results.river.reachs:
rk += r.geometry.get_rk()
write_float(rk)
# Z and Y (4th line)
# ltmp = []
# for r in results.river.reachs:
# for p in r.prifiles:
# ltpm.append(p.rk)
write_float(3*nb_profile*[0.0])
# Data
for timestamp in ts_list:
q = list(
map(
lambda p: p.get_ts_key(timestamp, "Q"),
profiles
)
)
write_data(nb_profile, timestamp, "Q", q)
z = list(
map(
lambda p: p.get_ts_key(timestamp, "Z"),
profiles
)
)
write_data(nb_profile, timestamp, "Z", z)
logger.info(f"write_bin: ... end with {len(ts_list)} timestamps")
@timer
def results(self, study, repertory, qlog=None, name=None):
results = Results( results = Results(
study=study, study=study,
solver=self, solver=self,
repertory=repertory, repertory=repertory,
name=name, name=name,
) )
if name is None:
name = study.name.replace(" ", "_")
results_file = f"profil.{name}" results_file = f"profil.{name}"
fname = os.path.join(repertory, results_file) fname = os.path.join(repertory, results_file)
+1 -1
View File
@@ -55,7 +55,7 @@ class AboutWindow(PamhyrDialog):
label = self.get_label_text("label_version") label = self.get_label_text("label_version")
label = label.replace("@version", version) label = label.replace("@version", version)
label = label.replace("@codename", "(Adis-TS)") label = label.replace("@codename", "")
self.set_label_text("label_version", label) self.set_label_text("label_version", label)
# Authors # Authors
+1 -1
View File
@@ -54,7 +54,7 @@ class Plot(PamhyrPlot):
self._isometric_axis = False self._isometric_axis = False
self._auto_relim_update = True self._auto_relim_update = True
self._autoscale_update = True self._autoscale_update = False
def custom_ticks(self): def custom_ticks(self):
if self.data.header[0] != "time": if self.data.header[0] != "time":
+3
View File
@@ -107,6 +107,9 @@ class TableModel(PamhyrTableModel):
if not index.isValid() or role != Qt.EditRole: if not index.isValid() or role != Qt.EditRole:
return False return False
if self.is_same_data(index, value):
return False
row = index.row() row = index.row()
column = index.column() column = index.column()
+3
View File
@@ -167,6 +167,9 @@ class TableModel(PamhyrTableModel):
if not index.isValid() or role != Qt.EditRole: if not index.isValid() or role != Qt.EditRole:
return False return False
if self.is_same_data(index, value):
return False
row = index.row() row = index.row()
column = index.column() column = index.column()
@@ -55,7 +55,7 @@ class Plot(PamhyrPlot):
self._isometric_axis = False self._isometric_axis = False
self._auto_relim_update = True self._auto_relim_update = True
self._autoscale_update = True self._autoscale_update = False
def custom_ticks(self): def custom_ticks(self):
if self.data.header[0] != "time": if self.data.header[0] != "time":
@@ -50,6 +50,22 @@ logger = logging.getLogger()
class TableModel(PamhyrTableModel): class TableModel(PamhyrTableModel):
def get_true_data_row(self, row):
if len(self._data.data) > 0:
bc = self._data.data[row]
else:
return 0
return next(
map(
lambda e: e[0],
filter(
lambda e: e[1] == bc,
enumerate(self._data._data)
)
), 0
)
def data(self, index, role): def data(self, index, role):
if role == Qt.TextAlignmentRole: if role == Qt.TextAlignmentRole:
return Qt.AlignHCenter | Qt.AlignVCenter return Qt.AlignHCenter | Qt.AlignVCenter
@@ -63,6 +79,7 @@ class TableModel(PamhyrTableModel):
value = QVariant() value = QVariant()
if 0 <= column < 2: if 0 <= column < 2:
row = self.get_true_data_row(row)
v = self._data._data[row][column] v = self._data._data[row][column]
if self._data._types[column] == float: if self._data._types[column] == float:
value = f"{v:.4f}" value = f"{v:.4f}"
@@ -80,10 +97,15 @@ class TableModel(PamhyrTableModel):
if not index.isValid() or role != Qt.EditRole: if not index.isValid() or role != Qt.EditRole:
return False return False
if self.is_same_data(index, value):
return False
row = index.row() row = index.row()
column = index.column() column = index.column()
try: try:
row = self.get_true_data_row(row)
self._undo.push( self._undo.push(
SetDataCommand( SetDataCommand(
self._data, row, column, value self._data, row, column, value
@@ -110,14 +132,20 @@ class TableModel(PamhyrTableModel):
def delete(self, rows, parent=QModelIndex()): def delete(self, rows, parent=QModelIndex()):
self.beginRemoveRows(parent, rows[0], rows[-1]) self.beginRemoveRows(parent, rows[0], rows[-1])
_rows = list(
map(
lambda r: self.get_true_data_row(r),
rows
)
)
self._undo.push( self._undo.push(
DelCommand( DelCommand(
self._data, rows self._data, _rows
) )
) )
self.endRemoveRows() self.endRemoveRows()
self.layoutChanged.emit()
def sort(self, _reverse, parent=QModelIndex()): def sort(self, _reverse, parent=QModelIndex()):
self.layoutAboutToBeChanged.emit() self.layoutAboutToBeChanged.emit()
@@ -61,9 +61,7 @@ class AddCommand(QUndoCommand):
def redo(self): def redo(self):
if self._new is None: if self._new is None:
self._new = self._data.insert(self._index, ( self._new = self._data.add(self._index)
self._data._types[0](0), self._data._types[1](0.0)
))
else: else:
self._data.insert(self._index, self._new) self._data.insert(self._index, self._new)
@@ -82,10 +80,11 @@ class DelCommand(QUndoCommand):
def undo(self): def undo(self):
for row, el in self._bc: for row, el in self._bc:
self._data.insert(row, el) el.set_as_not_deleted()
def redo(self): def redo(self):
self._data.delete_i(self._rows) for row, el in self._bc:
el.set_as_deleted()
class PasteCommand(QUndoCommand): class PasteCommand(QUndoCommand):
@@ -63,6 +63,16 @@ class EditBoundaryConditionWindow(PamhyrWindow):
name = trad[self._pamhyr_name] name = trad[self._pamhyr_name]
if self._data is not None:
n = self._data.node
node_name = next(filter(
lambda x: x.id == n, study.river._nodes
)).name
name += (
f" - {study.name} " +
f"({node_name})"
)
super(EditBoundaryConditionWindow, self).__init__( super(EditBoundaryConditionWindow, self).__init__(
title=name, title=name,
study=study, study=study,
@@ -71,16 +81,6 @@ class EditBoundaryConditionWindow(PamhyrWindow):
parent=parent parent=parent
) )
if self._data is not None:
n = self._data.node
node_name = next(filter(
lambda x: x.id == n, self._study.river._nodes
)).name
name += (
f" - {study.name} " +
f"({node_name})"
)
self._hash_data.append(data) self._hash_data.append(data)
self.setup_table() self.setup_table()
@@ -89,6 +89,12 @@ class EditBoundaryConditionWindow(PamhyrWindow):
def setup_table(self): def setup_table(self):
table_headers = self._trad.get_dict("table_headers") table_headers = self._trad.get_dict("table_headers")
if self._study.is_editable():
editable_headers = self._trad.get_dict("table_headers")
else:
editable_headers = []
if self._data.type == "Concentration": if self._data.type == "Concentration":
self._data.header = ["time", "concentration"] self._data.header = ["time", "concentration"]
else: else:
@@ -108,7 +114,7 @@ class EditBoundaryConditionWindow(PamhyrWindow):
self._table = TableModel( self._table = TableModel(
table_view=table, table_view=table,
table_headers=headers, table_headers=headers,
editable_headers=self._data.header, editable_headers=editable_headers,
delegates={ delegates={
# "time": self._delegate_time, # "time": self._delegate_time,
}, },
@@ -141,9 +147,10 @@ class EditBoundaryConditionWindow(PamhyrWindow):
self.plot.draw() self.plot.draw()
def setup_connections(self): def setup_connections(self):
self.find(QAction, "action_add").triggered.connect(self.add) if self._study.is_editable():
self.find(QAction, "action_del").triggered.connect(self.delete) self.find(QAction, "action_add").triggered.connect(self.add)
self.find(QAction, "action_sort").triggered.connect(self.sort) self.find(QAction, "action_del").triggered.connect(self.delete)
self.find(QAction, "action_sort").triggered.connect(self.sort)
self._table.dataChanged.connect(self.update) self._table.dataChanged.connect(self.update)
def update(self): def update(self):
+55 -43
View File
@@ -43,7 +43,7 @@ from View.Tools.PamhyrTable import PamhyrTableModel
from View.BoundaryConditionsAdisTS.UndoCommand import ( from View.BoundaryConditionsAdisTS.UndoCommand import (
SetNodeCommand, SetTypeCommand, SetNodeCommand, SetTypeCommand,
AddCommand, DelCommand, SetPolCommand AddCommand, DelCommand
) )
from View.BoundaryCondition.translate import BC_types from View.BoundaryCondition.translate import BC_types
@@ -112,18 +112,37 @@ class ComboBoxDelegate(QItemDelegate):
class TableModel(PamhyrTableModel): class TableModel(PamhyrTableModel):
def __init__(self, pollutant=None, bc_list=None, trad=None, **kwargs): def __init__(self, bc_list=None, pollutant_bc_list=None,
trad=None, **kwargs):
self._trad = trad self._trad = trad
self._bc_list = bc_list self._bc_list = bc_list
self._pollutant = pollutant self._pollutant = pollutant_bc_list.id
super(TableModel, self).__init__(trad=trad, **kwargs) super(TableModel, self).__init__(trad=trad, **kwargs)
def rowCount(self, parent): def _setup_lst(self):
return len(self._bc_list) self._lst = list(
filter(
lambda bc: bc.pollutant == self._pollutant,
self._bc_list.lst
)
)
def get(self, row):
if row < 0 or row >= len(self._lst):
return None
return self._lst[row]
def _global_row(self, row):
bc = self.get(row)
if bc is None:
return None
return self._bc_list.index(bc)
def rowCount(self, parent=QModelIndex()):
return len(self._lst)
def data(self, index, role): def data(self, index, role):
if role != Qt.ItemDataRole.DisplayRole: if role != Qt.ItemDataRole.DisplayRole:
return QVariant() return QVariant()
@@ -131,12 +150,12 @@ class TableModel(PamhyrTableModel):
column = index.column() column = index.column()
if self._headers[column] == "type": if self._headers[column] == "type":
n = self._bc_list.get(row).type n = self._lst[row].type
if n is None or n == "": if n is None or n == "":
return self._trad["not_associated"] return self._trad["not_associated"]
return n return n
elif self._headers[column] == "node": elif self._headers[column] == "node":
n = self._bc_list.get(row).node n = self._lst[row].node
if n is None: if n is None:
return self._trad["not_associated"] return self._trad["not_associated"]
tmp = next(filter(lambda x: x.id == n, self._data._nodes), None) tmp = next(filter(lambda x: x.id == n, self._data._nodes), None)
@@ -144,18 +163,6 @@ class TableModel(PamhyrTableModel):
return tmp.name return tmp.name
else: else:
return self._trad["not_associated"] return self._trad["not_associated"]
elif self._headers[column] == "pol":
n = self._bc_list.get(row).pollutant
if n is None or n == "not_associated" or n == "":
return self._trad["not_associated"]
tmp = next(filter(lambda x: x.id == n,
self._data._Pollutants.Pollutants_List
),
None)
if tmp is not None:
return tmp.name
else:
return self._trad["not_associated"]
return QVariant() return QVariant()
@@ -163,38 +170,32 @@ class TableModel(PamhyrTableModel):
if not index.isValid() or role != Qt.EditRole: if not index.isValid() or role != Qt.EditRole:
return False return False
if self.is_same_data(index, value):
return False
row = index.row() row = index.row()
column = index.column() column = index.column()
try: try:
if self._headers[column] == "type": if self._headers[column] == "type":
global_row = self._global_row(row)
self._undo.push( self._undo.push(
SetTypeCommand( SetTypeCommand(
self._bc_list, row, value self._bc_list, global_row, value
) )
) )
elif self._headers[column] == "node": elif self._headers[column] == "node":
global_row = self._global_row(row)
node = next(
filter(lambda n: n.name == value, self._data.nodes()),
None
)
self._undo.push( self._undo.push(
SetNodeCommand( SetNodeCommand(
self._bc_list, row, self._data.node(value) self._bc_list, global_row, node
) )
) )
elif self._headers[column] == "pol":
if value == self._trad["not_associated"]:
self._undo.push(
SetPolCommand(
self._bc_list, row, None
)
)
else:
pol = next(filter(lambda x: x.name == value,
self._data._Pollutants.Pollutants_List)
)
self._undo.push(
SetPolCommand(
self._bc_list, row, pol.id
)
)
except Exception as e: except Exception as e:
logger.info(e) logger.info(e)
logger.debug(traceback.format_exc()) logger.debug(traceback.format_exc())
@@ -203,33 +204,44 @@ class TableModel(PamhyrTableModel):
return True return True
def add(self, row, parent=QModelIndex()): def add(self, row, parent=QModelIndex()):
self.beginInsertRows(parent, row, row - 1) row = len(self._lst)
self.beginInsertRows(parent, row, row)
self._undo.push( self._undo.push(
AddCommand( AddCommand(
self._pollutant, self._bc_list, row self._pollutant, self._bc_list, len(self._bc_list)
) )
) )
self._setup_lst()
self.endInsertRows() self.endInsertRows()
self.layoutChanged.emit() self.layoutChanged.emit()
def delete(self, rows, parent=QModelIndex()): def delete(self, rows, parent=QModelIndex()):
self.beginRemoveRows(parent, rows[0], rows[-1]) self.beginRemoveRows(parent, rows[0], rows[-1])
row_by_bc = {
id(bc): row for row, bc in enumerate(self._bc_list._lst)
}
global_rows = [
row_by_bc[id(self._lst[row])]
for row in rows
if 0 <= row < len(self._lst)
]
self._undo.push( self._undo.push(
DelCommand( DelCommand(
self._bc_list, rows self._bc_list, global_rows
) )
) )
self._setup_lst()
self.endRemoveRows() self.endRemoveRows()
self.layoutChanged.emit() self.layoutChanged.emit()
def undo(self): def undo(self):
self._undo.undo() self._undo.undo()
self._setup_lst()
self.layoutChanged.emit() self.layoutChanged.emit()
def redo(self): def redo(self):
self._undo.redo() self._undo.redo()
self._setup_lst()
self.layoutChanged.emit() self.layoutChanged.emit()
@@ -36,7 +36,7 @@ class SetNodeCommand(QUndoCommand):
self._bcs = bcs self._bcs = bcs
self._index = index self._index = index
self._old = self._bcs.get(self._index).node self._old = self._bcs.get(self._index).node
self._new = node.id self._new = node.id if node is not None else None
def undo(self): def undo(self):
self._bcs.get(self._index).node = self._old self._bcs.get(self._index).node = self._old
@@ -62,22 +62,6 @@ class SetTypeCommand(QUndoCommand):
self._bcs.get(self._index).type = self._new self._bcs.get(self._index).type = self._new
class SetPolCommand(QUndoCommand):
def __init__(self, bcs, index, pollutant):
QUndoCommand.__init__(self)
self._bcs = bcs
self._index = index
self._old = self._bcs.get(self._index).pollutant
self._new = pollutant
def undo(self):
self._bcs.get(self._index).pollutant = self._old
def redo(self):
self._bcs.get(self._index).pollutant = self._new
class AddCommand(QUndoCommand): class AddCommand(QUndoCommand):
def __init__(self, pollutant, bcs, index): def __init__(self, pollutant, bcs, index):
QUndoCommand.__init__(self) QUndoCommand.__init__(self)
@@ -106,7 +90,7 @@ class DelCommand(QUndoCommand):
self._bc = [] self._bc = []
for row in rows: for row in rows:
self._bc.append((row, self._bcs.get(row))) self._bc.append((row, self._bcs._lst[row]))
self._bc.sort() self._bc.sort()
def undo(self): def undo(self):
+26 -17
View File
@@ -58,11 +58,22 @@ class BoundaryConditionAdisTSWindow(PamhyrWindow):
_pamhyr_ui = "BoundaryConditionsAdisTS" _pamhyr_ui = "BoundaryConditionsAdisTS"
_pamhyr_name = "Boundary conditions AdisTS" _pamhyr_name = "Boundary conditions AdisTS"
def __init__(self, study=None, config=None, parent=None): def __init__(self, data=None, pollutant_id=None, study=None,
config=None, parent=None):
self._data = data
self._pollutant_id = pollutant_id
trad = BCAdisTSTranslate() trad = BCAdisTSTranslate()
self._pollutant_name = next(
(x.name for x in study._river._Pollutants.Pollutants_List
if x.id == self._pollutant_id),
None
)
name = ( name = (
trad[self._pamhyr_name] + trad[self._pamhyr_name] +
" - " + study.name " - " + study.name +
" - " + self._pollutant_name
) )
super(BoundaryConditionAdisTSWindow, self).__init__( super(BoundaryConditionAdisTSWindow, self).__init__(
@@ -73,15 +84,12 @@ class BoundaryConditionAdisTSWindow(PamhyrWindow):
parent=parent parent=parent
) )
self._pollutants_lst = self._study._river._Pollutants
self._bcs = self._study.river.boundary_conditions_adists self._bcs = self._study.river.boundary_conditions_adists
self.setup_graph() self.setup_graph()
self.setup_table() self.setup_table()
self.setup_connections() self.setup_connections()
self.ui.setWindowTitle(self._title)
def setup_table(self): def setup_table(self):
self._delegate_type = ComboBoxDelegate( self._delegate_type = ComboBoxDelegate(
trad=self._trad, trad=self._trad,
@@ -95,25 +103,24 @@ class BoundaryConditionAdisTSWindow(PamhyrWindow):
mode="node", mode="node",
parent=self parent=self
) )
self._delegate_pol = ComboBoxDelegate(
trad=self._trad, if self._study.is_editable():
data=self._study.river, editable_headers = self._trad.get_dict("table_headers")
mode="pol", else:
parent=self editable_headers = []
)
table = self.find(QTableView, f"tableView") table = self.find(QTableView, f"tableView")
self._table = TableModel( self._table = TableModel(
table_view=table, table_view=table,
table_headers=self._trad.get_dict("table_headers"), table_headers=self._trad.get_dict("table_headers"),
editable_headers=["type", "node", "pol"], editable_headers=editable_headers,
delegates={ delegates={
"type": self._delegate_type, "type": self._delegate_type,
"node": self._delegate_node, "node": self._delegate_node,
"pol": self._delegate_pol,
}, },
trad=self._trad, trad=self._trad,
bc_list=self._study.river.boundary_conditions_adists, bc_list=self._study.river.boundary_conditions_adists,
pollutant_bc_list=self._data,
undo=self._undo_stack, undo=self._undo_stack,
data=self._study.river data=self._study.river
) )
@@ -133,8 +140,10 @@ class BoundaryConditionAdisTSWindow(PamhyrWindow):
self.graph_layout.addWidget(self.graph_widget) self.graph_layout.addWidget(self.graph_widget)
def setup_connections(self): def setup_connections(self):
self.find(QAction, "action_add").triggered.connect(self.add) if self._study.is_editable():
self.find(QAction, "action_del").triggered.connect(self.delete) self.find(QAction, "action_add").triggered.connect(self.add)
self.find(QAction, "action_del").triggered.connect(self.delete)
self.find(QAction, "action_edit").triggered.connect(self.edit) self.find(QAction, "action_edit").triggered.connect(self.edit)
def index_selected_row(self): def index_selected_row(self):
@@ -156,7 +165,7 @@ class BoundaryConditionAdisTSWindow(PamhyrWindow):
) )
def add(self): def add(self):
self._table.add(len(self._bcs)) self._table.add(self._table.rowCount())
def delete(self): def delete(self):
rows = self.index_selected_rows() rows = self.index_selected_rows()
@@ -180,7 +189,7 @@ class BoundaryConditionAdisTSWindow(PamhyrWindow):
def edit(self): def edit(self):
rows = self.index_selected_rows() rows = self.index_selected_rows()
for row in rows: for row in rows:
data = self._bcs.get(row) data = self._table.get(row)
if data.node is None: if data.node is None:
continue continue
@@ -34,5 +34,4 @@ class BCAdisTSTranslate(MainTranslate):
self._sub_dict["table_headers"] = { self._sub_dict["table_headers"] = {
"type": self._dict["type"], "type": self._dict["type"],
"node": _translate("BoundaryCondition", "Node"), "node": _translate("BoundaryCondition", "Node"),
"pol": _translate("BoundaryCondition", "Pollutant")
} }
+31 -8
View File
@@ -46,25 +46,28 @@ _translate = QCoreApplication.translate
class ComboBoxDelegate(QItemDelegate): class ComboBoxDelegate(QItemDelegate):
def __init__(self, data=None, ic_spec_lst=None, def __init__(self, data=None, d90_spec_lst=None,
trad=None, parent=None, mode="reaches"): trad=None, parent=None, mode="reaches"):
super(ComboBoxDelegate, self).__init__(parent) super(ComboBoxDelegate, self).__init__(parent)
self._data = data self._data = data
self._mode = mode self._mode = mode
self._trad = trad self._trad = trad
self._ic_spec_lst = ic_spec_lst self._d90_spec_lst = d90_spec_lst
def createEditor(self, parent, option, index): def createEditor(self, parent, option, index):
self.editor = QComboBox(parent) self.editor = QComboBox(parent)
val = [] val = []
if self._mode == "rk": if self._mode == "rk":
reach_id = self._ic_spec_lst[index.row()].reach reach_id = self._d90_spec_lst[index.row()].reach
reach = next(filter( reach = next(
lambda edge: edge.id == reach_id, self._data.edges() filter(
)) lambda edge: edge.id == reach_id, self._data.edges()
),
None
)
if reach_id is not None: if reach_id is not None:
val = list( val = list(
@@ -116,9 +119,16 @@ class D90TableModel(PamhyrTableModel):
super(D90TableModel, self).__init__(data=data, **kwargs) super(D90TableModel, self).__init__(data=data, **kwargs)
self._data = data self._data = data
if self._undo is not None:
self._undo.indexChanged.connect(lambda _: self.update())
def _setup_lst(self): def _setup_lst(self):
self._lst = self._data._data self._lst = list(
filter(
lambda d90: d90._deleted is False,
self._data._data
)
)
def rowCount(self, parent): def rowCount(self, parent):
return len(self._lst) return len(self._lst)
@@ -164,6 +174,9 @@ class D90TableModel(PamhyrTableModel):
if not index.isValid() or role != Qt.EditRole: if not index.isValid() or role != Qt.EditRole:
return False return False
if self.is_same_data(index, value):
return False
row = index.row() row = index.row()
column = index.column() column = index.column()
@@ -197,25 +210,35 @@ class D90TableModel(PamhyrTableModel):
) )
) )
self._setup_lst()
self.endInsertRows() self.endInsertRows()
self.layoutChanged.emit() self.layoutChanged.emit()
def delete(self, rows, parent=QModelIndex()): def delete(self, rows, parent=QModelIndex()):
self.beginRemoveRows(parent, rows[0], rows[-1]) self.beginRemoveRows(parent, rows[0], rows[-1])
data_rows = {
id(d90): i for i, d90 in enumerate(self._data._data)
}
self._undo.push( self._undo.push(
DelCommand( DelCommand(
self._data, self._lst, rows self._data,
self._data._data,
[data_rows[id(self._lst[row])] for row in rows
if 0 <= row < len(self._lst)]
) )
) )
self._setup_lst()
self.endRemoveRows() self.endRemoveRows()
self.layoutChanged.emit() self.layoutChanged.emit()
def undo(self): def undo(self):
self._undo.undo() self._undo.undo()
self._setup_lst()
self.layoutChanged.emit() self.layoutChanged.emit()
def redo(self): def redo(self):
self._undo.redo() self._undo.redo()
self._setup_lst()
self.layoutChanged.emit() self.layoutChanged.emit()
+8 -9
View File
@@ -112,11 +112,11 @@ class SetCommandSpec(QUndoCommand):
class AddCommand(QUndoCommand): class AddCommand(QUndoCommand):
def __init__(self, data, ics_spec, index): def __init__(self, data, d90_spec, index):
QUndoCommand.__init__(self) QUndoCommand.__init__(self)
self._data = data self._data = data
self._ics_spec = ics_spec self._d90_spec = d90_spec
self._index = index self._index = index
self._new = None self._new = None
@@ -131,21 +131,20 @@ class AddCommand(QUndoCommand):
class DelCommand(QUndoCommand): class DelCommand(QUndoCommand):
def __init__(self, data, ics_spec, rows): def __init__(self, data, d90_spec, rows):
QUndoCommand.__init__(self) QUndoCommand.__init__(self)
self._data = data self._data = data
self._ics_spec = ics_spec self._d90_spec = d90_spec
self._rows = rows self._rows = rows
# self._data = data
self._ic = [] self._d90 = []
for row in rows: for row in rows:
self._ic.append((row, self._ics_spec[row])) self._d90.append((row, self._d90_spec[row]))
self._ic.sort() self._d90.sort()
def undo(self): def undo(self):
for row, el in self._ic: for row, el in self._d90:
self._data.insert(row, el) self._data.insert(row, el)
def redo(self): def redo(self):
+44 -15
View File
@@ -43,7 +43,7 @@ from PyQt5.QtWidgets import (
from Modules import Modules from Modules import Modules
from View.InitialConditionsAdisTS.UndoCommand import ( from View.D90AdisTS.UndoCommand import (
SetCommand, SetCommand,
) )
@@ -93,18 +93,21 @@ class D90AdisTSWindow(PamhyrWindow):
self.setup_table() self.setup_table()
self.ui.setWindowTitle(self._title)
def setup_table(self): def setup_table(self):
path_icons = os.path.join(self._get_ui_directory(), f"ressources") path_icons = os.path.join(self._get_ui_directory(), f"ressources")
if self._study.is_editable():
editable_headers = self._trad.get_dict("table_headers")
else:
editable_headers = []
table_default = self.find(QTableView, f"tableView") table_default = self.find(QTableView, f"tableView")
self._table = D90TableDefaultModel( self._table = D90TableDefaultModel(
table_view=table_default, table_view=table_default,
table_headers=self._trad.get_dict("table_headers"), table_headers=self._trad.get_dict("table_headers"),
editable_headers=["name", "d90"], editable_headers=editable_headers,
delegates={}, delegates={},
data=self._data, data=self._data,
undo=self._undo_stack, undo=self._undo_stack,
@@ -124,10 +127,15 @@ class D90AdisTSWindow(PamhyrWindow):
action_add = QAction(self) action_add = QAction(self)
action_add.setIcon(QIcon(os.path.join(path_icons, f"add.png"))) action_add.setIcon(QIcon(os.path.join(path_icons, f"add.png")))
action_add.triggered.connect(self.add)
if self._study.is_editable():
action_add.triggered.connect(self.add)
action_delete = QAction(self) action_delete = QAction(self)
action_delete.setIcon(QIcon(os.path.join(path_icons, f"del.png"))) action_delete.setIcon(QIcon(os.path.join(path_icons, f"del.png")))
action_delete.triggered.connect(self.delete)
if self._study.is_editable():
action_delete.triggered.connect(self.delete)
toolBar.addAction(action_add) toolBar.addAction(action_add)
toolBar.addAction(action_delete) toolBar.addAction(action_delete)
@@ -138,22 +146,27 @@ class D90AdisTSWindow(PamhyrWindow):
self._delegate_reach = ComboBoxDelegate( self._delegate_reach = ComboBoxDelegate(
trad=self._trad, trad=self._trad,
data=self._study.river, data=self._study.river,
ic_spec_lst=self._data[0]._data, d90_spec_lst=self._data[0]._data,
parent=self, parent=self,
mode="reaches" mode="reaches"
) )
self._delegate_rk = ComboBoxDelegate( self._delegate_rk = ComboBoxDelegate(
trad=self._trad, trad=self._trad,
data=self._study.river, data=self._study.river,
ic_spec_lst=self._data[0]._data, d90_spec_lst=self._data[0]._data,
parent=self, parent=self,
mode="rk" mode="rk"
) )
if self._study.is_editable():
editable_headers_spec = self._trad.get_dict("table_headers_spec")
else:
editable_headers_spec = []
self._table_spec = D90TableModel( self._table_spec = D90TableModel(
table_view=self.table_spec, table_view=self.table_spec,
table_headers=self._trad.get_dict("table_headers_spec"), table_headers=self._trad.get_dict("table_headers_spec"),
editable_headers=["name", "reach", "start_rk", "end_rk", "d90"], editable_headers=editable_headers_spec,
delegates={ delegates={
"reach": self._delegate_reach, "reach": self._delegate_reach,
"start_rk": self._delegate_rk, "start_rk": self._delegate_rk,
@@ -227,15 +240,15 @@ class D90AdisTSWindow(PamhyrWindow):
table = list( table = list(
map( map(
lambda eic: list( lambda ed90: list(
map( map(
lambda k: eic[1][k], lambda k: ed90[1][k],
["rk", "discharge", "elevation"] ["rk", "discharge", "elevation"]
) )
), ),
filter( filter(
lambda eic: eic[0] in rows, lambda ed90: ed90[0] in rows,
enumerate(self._ics.lst()) enumerate(self._d90.lst())
) )
) )
) )
@@ -264,11 +277,27 @@ class D90AdisTSWindow(PamhyrWindow):
self._update() self._update()
def _undo(self): def _undo(self):
self._table.undo() undo_stack = self._undo_stack
if undo_stack is None or not undo_stack.canUndo():
return
if isinstance(undo_stack.command(undo_stack.index() - 1), SetCommand):
self._table.undo()
else:
self._table_spec.undo()
self._update() self._update()
def _redo(self): def _redo(self):
self._table.redo() undo_stack = self._undo_stack
if undo_stack is None or not undo_stack.canRedo():
return
if isinstance(undo_stack.command(undo_stack.index()), SetCommand):
self._table.redo()
else:
self._table_spec.redo()
self._update() self._update()
def add(self): def add(self):
+35 -10
View File
@@ -46,25 +46,28 @@ _translate = QCoreApplication.translate
class ComboBoxDelegate(QItemDelegate): class ComboBoxDelegate(QItemDelegate):
def __init__(self, data=None, ic_spec_lst=None, def __init__(self, data=None, dif_spec_lst=None,
trad=None, parent=None, mode="reaches"): trad=None, parent=None, mode="reaches"):
super(ComboBoxDelegate, self).__init__(parent) super(ComboBoxDelegate, self).__init__(parent)
self._data = data self._data = data
self._mode = mode self._mode = mode
self._trad = trad self._trad = trad
self._ic_spec_lst = ic_spec_lst self._dif_spec_lst = dif_spec_lst
def createEditor(self, parent, option, index): def createEditor(self, parent, option, index):
self.editor = QComboBox(parent) self.editor = QComboBox(parent)
val = [] val = []
if self._mode == "rk": if self._mode == "rk":
reach_id = self._ic_spec_lst[index.row()].reach reach_id = self._dif_spec_lst[index.row()].reach
reach = next(filter( reach = next(
lambda edge: edge.id == reach_id, self._data.edges() filter(
)) lambda edge: edge.id == reach_id, self._data.edges()
),
None
)
if reach_id is not None: if reach_id is not None:
val = list( val = list(
@@ -118,9 +121,16 @@ class DIFTableModel(PamhyrTableModel):
super(DIFTableModel, self).__init__(data=data, **kwargs) super(DIFTableModel, self).__init__(data=data, **kwargs)
self._data = data self._data = data
if self._undo is not None:
self._undo.indexChanged.connect(lambda _: self.update())
def _setup_lst(self): def _setup_lst(self):
self._lst = self._data._data self._lst = list(
filter(
lambda dif: dif._deleted is False,
self._data._data
)
)
def rowCount(self, parent): def rowCount(self, parent):
return len(self._lst) return len(self._lst)
@@ -176,6 +186,9 @@ class DIFTableModel(PamhyrTableModel):
if not index.isValid() or role != Qt.EditRole: if not index.isValid() or role != Qt.EditRole:
return False return False
if self.is_same_data(index, value):
return False
row = index.row() row = index.row()
column = index.column() column = index.column()
@@ -183,7 +196,10 @@ class DIFTableModel(PamhyrTableModel):
if self._headers[column] != "reach": if self._headers[column] != "reach":
self._undo.push( self._undo.push(
SetCommandSpec( SetCommandSpec(
self._lst, row, self._headers[column], value self._lst,
row,
self._headers[column],
value
) )
) )
elif self._headers[column] == "reach": elif self._headers[column] == "reach":
@@ -211,25 +227,34 @@ class DIFTableModel(PamhyrTableModel):
) )
) )
self._setup_lst()
self.endInsertRows() self.endInsertRows()
self.layoutChanged.emit() self.layoutChanged.emit()
def delete(self, rows, parent=QModelIndex()): def delete(self, rows, parent=QModelIndex()):
self.beginRemoveRows(parent, rows[0], rows[-1]) self.beginRemoveRows(parent, rows[0], rows[-1])
data_rows = {
id(dif): i for i, dif in enumerate(self._data._data)
}
self._undo.push( self._undo.push(
DelCommand( DelCommand(
self._data, self._lst, rows self._data,
self._data._data,
[data_rows[id(self._lst[row])] for row in rows
if 0 <= row < len(self._lst)]
) )
) )
self._setup_lst()
self.endRemoveRows() self.endRemoveRows()
self.layoutChanged.emit() self.layoutChanged.emit()
def undo(self): def undo(self):
self._undo.undo() self._undo.undo()
self._setup_lst()
self.layoutChanged.emit() self.layoutChanged.emit()
def redo(self): def redo(self):
self._undo.redo() self._undo.redo()
self._setup_lst()
self.layoutChanged.emit() self.layoutChanged.emit()
+8 -9
View File
@@ -136,11 +136,11 @@ class SetCommandSpec(QUndoCommand):
class AddCommand(QUndoCommand): class AddCommand(QUndoCommand):
def __init__(self, data, ics_spec, index): def __init__(self, data, dif_spec, index):
QUndoCommand.__init__(self) QUndoCommand.__init__(self)
self._data = data self._data = data
self._ics_spec = ics_spec self._dif_spec = dif_spec
self._index = index self._index = index
self._new = None self._new = None
@@ -155,21 +155,20 @@ class AddCommand(QUndoCommand):
class DelCommand(QUndoCommand): class DelCommand(QUndoCommand):
def __init__(self, data, ics_spec, rows): def __init__(self, data, dif_spec, rows):
QUndoCommand.__init__(self) QUndoCommand.__init__(self)
self._data = data self._data = data
self._ics_spec = ics_spec self._dif_spec = dif_spec
self._rows = rows self._rows = rows
# self._data = data
self._ic = [] self._dif = []
for row in rows: for row in rows:
self._ic.append((row, self._ics_spec[row])) self._dif.append((row, self._dif_spec[row]))
self._ic.sort() self._dif.sort()
def undo(self): def undo(self):
for row, el in self._ic: for row, el in self._dif:
self._data.insert(row, el) self._data.insert(row, el)
def redo(self): def redo(self):
+39 -12
View File
@@ -51,6 +51,8 @@ from View.DIFAdisTS.Table import (
DIFTableModel, ComboBoxDelegate, DIFTableModel, ComboBoxDelegate,
) )
from View.DIFAdisTS.UndoCommand import SetCommand
from View.DIFAdisTS.translate import DIFAdisTSTranslate from View.DIFAdisTS.translate import DIFAdisTSTranslate
from Solver.Mage import Mage8 from Solver.Mage import Mage8
@@ -89,18 +91,21 @@ class DIFAdisTSWindow(PamhyrWindow):
self.setup_table() self.setup_table()
self.ui.setWindowTitle(self._title)
def setup_table(self): def setup_table(self):
path_icons = os.path.join(self._get_ui_directory(), f"ressources") path_icons = os.path.join(self._get_ui_directory(), f"ressources")
table_default = self.find(QTableView, f"tableView") table_default = self.find(QTableView, f"tableView")
if self._study.is_editable():
editable_headers = self._trad.get_dict("table_headers")
else:
editable_headers = []
self._delegate_method = ComboBoxDelegate( self._delegate_method = ComboBoxDelegate(
trad=self._trad, trad=self._trad,
data=self._study.river, data=self._study.river,
ic_spec_lst=self._data[0]._data, dif_spec_lst=self._data[0]._data,
parent=self, parent=self,
mode="method" mode="method"
) )
@@ -108,7 +113,7 @@ class DIFAdisTSWindow(PamhyrWindow):
self._table = DIFTableDefaultModel( self._table = DIFTableDefaultModel(
table_view=table_default, table_view=table_default,
table_headers=self._trad.get_dict("table_headers"), table_headers=self._trad.get_dict("table_headers"),
editable_headers=["method", "dif", "b", "c"], editable_headers=editable_headers,
delegates={ delegates={
"method": self._delegate_method "method": self._delegate_method
}, },
@@ -130,10 +135,14 @@ class DIFAdisTSWindow(PamhyrWindow):
action_add = QAction(self) action_add = QAction(self)
action_add.setIcon(QIcon(os.path.join(path_icons, f"add.png"))) action_add.setIcon(QIcon(os.path.join(path_icons, f"add.png")))
action_add.triggered.connect(self.add)
if self._study.is_editable():
action_add.triggered.connect(self.add)
action_delete = QAction(self) action_delete = QAction(self)
action_delete.setIcon(QIcon(os.path.join(path_icons, f"del.png"))) action_delete.setIcon(QIcon(os.path.join(path_icons, f"del.png")))
action_delete.triggered.connect(self.delete)
if self._study.is_editable():
action_delete.triggered.connect(self.delete)
toolBar.addAction(action_add) toolBar.addAction(action_add)
toolBar.addAction(action_delete) toolBar.addAction(action_delete)
@@ -144,23 +153,27 @@ class DIFAdisTSWindow(PamhyrWindow):
self._delegate_reach = ComboBoxDelegate( self._delegate_reach = ComboBoxDelegate(
trad=self._trad, trad=self._trad,
data=self._study.river, data=self._study.river,
ic_spec_lst=self._data[0]._data, dif_spec_lst=self._data[0]._data,
parent=self, parent=self,
mode="reaches" mode="reaches"
) )
self._delegate_rk = ComboBoxDelegate( self._delegate_rk = ComboBoxDelegate(
trad=self._trad, trad=self._trad,
data=self._study.river, data=self._study.river,
ic_spec_lst=self._data[0]._data, dif_spec_lst=self._data[0]._data,
parent=self, parent=self,
mode="rk" mode="rk"
) )
if self._study.is_editable():
editable_headers_spec = self._trad.get_dict("table_headers_spec")
else:
editable_headers_spec = []
self._table_spec = DIFTableModel( self._table_spec = DIFTableModel(
table_view=self.table_spec, table_view=self.table_spec,
table_headers=self._trad.get_dict("table_headers_spec"), table_headers=self._trad.get_dict("table_headers_spec"),
editable_headers=["method", "reach", "start_rk", editable_headers=editable_headers_spec,
"end_rk", "dif", "b", "c"],
delegates={ delegates={
"method": self._delegate_method, "method": self._delegate_method,
"reach": self._delegate_reach, "reach": self._delegate_reach,
@@ -272,11 +285,25 @@ class DIFAdisTSWindow(PamhyrWindow):
self._update() self._update()
def _undo(self): def _undo(self):
self._table.undo() undo_stack = self._undo_stack
if undo_stack is None or not undo_stack.canUndo():
return
if isinstance(undo_stack.command(undo_stack.index() - 1), SetCommand):
self._table.undo()
else:
self._table_spec.undo()
self._update() self._update()
def _redo(self): def _redo(self):
self._table.redo() undo_stack = self._undo_stack
if undo_stack is None or not undo_stack.canRedo():
return
if isinstance(undo_stack.command(undo_stack.index()), SetCommand):
self._table.redo()
else:
self._table_spec.redo()
self._update() self._update()
def add(self): def add(self):
+1 -1
View File
@@ -43,7 +43,7 @@ class PlotRKZ(PlotRKC):
self._isometric_axis = False self._isometric_axis = False
self._auto_relim_update = True self._auto_relim_update = True
self._autoscale_update = True self._autoscale_update = False
self.parent = parent self.parent = parent
def onpick(self, event): def onpick(self, event):
+1 -1
View File
@@ -50,7 +50,7 @@ class PlotStricklers(PamhyrPlot):
self._auto_relim = False self._auto_relim = False
self._auto_relim_update = False self._auto_relim_update = False
self._autoscale_update = True self._autoscale_update = False
@timer @timer
def draw(self, highlight=None): def draw(self, highlight=None):
+3
View File
@@ -134,6 +134,9 @@ class FrictionTableModel(PamhyrTableModel):
if not index.isValid() or role != Qt.EditRole: if not index.isValid() or role != Qt.EditRole:
return False return False
if self.is_same_data(index, value):
return False
row = index.row() row = index.row()
column = index.column() column = index.column()
+5 -2
View File
@@ -16,7 +16,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from tools import trace, timer from tools import trace, timer, logger_exception
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QAction, QListView, QVBoxLayout, QAction, QListView, QVBoxLayout,
@@ -134,7 +134,10 @@ class GeoTIFFListWindow(PamhyrWindow):
def _update_rectangle(self): def _update_rectangle(self):
for rect in self._plot_rect: for rect in self._plot_rect:
rect.remove() try:
rect.remove()
except Exception as e:
logger_exception(e)
self._plot_rect = [] self._plot_rect = []
+2 -2
View File
@@ -38,7 +38,7 @@ class PlotAC(PamhyrPlot):
self._isometric_axis = False self._isometric_axis = False
self._auto_relim_update = True self._auto_relim_update = True
self._autoscale_update = True self._autoscale_update = False
self.label_x = self._trad["transverse_abscissa"] self.label_x = self._trad["transverse_abscissa"]
self.label_y = self._trad["unit_elevation"] self.label_y = self._trad["unit_elevation"]
@@ -143,7 +143,7 @@ class PlotAC(PamhyrPlot):
] ]
else: else:
color = self.color_incomplete_gl[ color = self.color_incomplete_gl[
lincomplete.index(txt) lincomplete.index(txt) % len(self.color_incomplete_gl)
] ]
annotation = self.canvas.axes.annotate( annotation = self.canvas.axes.annotate(
+3
View File
@@ -114,6 +114,9 @@ class GeometryProfileTableModel(PamhyrTableModel):
column = index.column() column = index.column()
if role == Qt.EditRole: if role == Qt.EditRole:
if self.is_same_data(index, value):
return False
try: try:
if column == 0: if column == 0:
self._undo.push( self._undo.push(
+32
View File
@@ -17,6 +17,10 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from View.Tools.PamhyrWindow import PamhyrDialog from View.Tools.PamhyrWindow import PamhyrDialog
from PyQt5.QtWidgets import QDoubleSpinBox
from View.Tools.FlexibleDoubleSpinBox import FlexibleDoubleSpinBox
from PyQt5.QtWidgets import QDoubleSpinBox
from View.Tools.FlexibleDoubleSpinBox import FlexibleDoubleSpinBox
class ShiftDialog(PamhyrDialog): class ShiftDialog(PamhyrDialog):
@@ -31,8 +35,36 @@ class ShiftDialog(PamhyrDialog):
parent=parent parent=parent
) )
self._replace_spinboxes()
self._init_default_values() self._init_default_values()
def _replace_spinboxes(self):
for name in ["doubleSpinBox_X", "doubleSpinBox_Y", "doubleSpinBox_Z"]:
old = self.find(QDoubleSpinBox, name)
if old is None:
continue
new = FlexibleDoubleSpinBox(old.parent())
new.setObjectName(old.objectName())
# Copier propriétés utiles
new.setDecimals(old.decimals())
new.setMinimum(old.minimum())
new.setMaximum(old.maximum())
new.setSingleStep(old.singleStep())
new.setValue(old.value())
new.setGeometry(old.geometry())
# Remplacement dans layout si présent
layout = old.parent().layout()
if layout is not None:
layout.replaceWidget(old, new)
old.hide()
old.setParent(None)
old.deleteLater()
def _init_default_values(self): def _init_default_values(self):
self._dx = 0.0 self._dx = 0.0
self._dy = 0.0 self._dy = 0.0
+3 -4
View File
@@ -95,6 +95,9 @@ class GeometryReachTableModel(PamhyrTableModel):
column = index.column() column = index.column()
if role == Qt.EditRole and index.column() != 2: if role == Qt.EditRole and index.column() != 2:
if self.is_same_data(index, value):
return False
try: try:
if index.column() == 0: if index.column() == 0:
self._undo.push( self._undo.push(
@@ -251,7 +254,6 @@ class GeometryReachTableModel(PamhyrTableModel):
self.layoutChanged.emit() self.layoutChanged.emit()
def meshing(self, mesher, data, tableView): def meshing(self, mesher, data, tableView):
new_profiles = mesher.meshing( new_profiles = mesher.meshing(
self._data, self._data,
**data **data
@@ -287,7 +289,6 @@ class GeometryReachTableModel(PamhyrTableModel):
self.layoutChanged.emit() self.layoutChanged.emit()
def purge(self, np_purge): def purge(self, np_purge):
self._undo.push( self._undo.push(
PurgeCommand( PurgeCommand(
self._data, np_purge self._data, np_purge
@@ -296,7 +297,6 @@ class GeometryReachTableModel(PamhyrTableModel):
self.layoutChanged.emit() self.layoutChanged.emit()
def shift(self, rows, dx, dy, dz): def shift(self, rows, dx, dy, dz):
self._undo.push( self._undo.push(
ShiftCommand( ShiftCommand(
self._data, rows, dx, dy, dz self._data, rows, dx, dy, dz
@@ -305,7 +305,6 @@ class GeometryReachTableModel(PamhyrTableModel):
self.layoutChanged.emit() self.layoutChanged.emit()
def change_reach(self, new_reach, parent): def change_reach(self, new_reach, parent):
self._undo.push( self._undo.push(
ChangeReachCommand( ChangeReachCommand(
new_reach, new_reach,
+18
View File
@@ -91,3 +91,21 @@ class GeometryTranslate(MainTranslate):
self._dict["Shift"] = _translate( self._dict["Shift"] = _translate(
"Geometry", "Shift" "Geometry", "Shift"
) )
self._dict["warning"] = _translate(
"Geometry", "Warning"
)
self._dict["format_not_exportable"] = _translate(
"Geometry", "The format of the file is not exportable."
)
self._dict["update_rk_error"] = _translate(
"Geometry",
"RK update can't be executed."
)
self._dict["update_rk_empty_profiles"] = _translate(
"Geometry",
"Some profiles don't contain any point."
)
self._dict["update_rk_empty_profiles_info"] = _translate(
"Geometry",
"Profiles concerned:"
)
+8 -10
View File
@@ -247,14 +247,13 @@ class UpdateRKCommand(QUndoCommand):
self._new_end_rk = None self._new_end_rk = None
def undo(self): def undo(self):
for rk, profile in zip(self._rks, self._reach.profiles):
profile.rk = rk self._reach.update_rks(self._rks)
for begin_rk, end_rk, friction in zip(self._begin_rk, for begin_rk, end_rk, friction in zip(self._begin_rk,
self._end_rk, self._end_rk,
self._frictions): self._frictions):
friction.begin_rk = begin_rk friction.update_rks(begin_rk, end_rk)
friction.end_rk = end_rk
def redo(self): def redo(self):
if self._new_rks is None: if self._new_rks is None:
@@ -264,13 +263,12 @@ class UpdateRKCommand(QUndoCommand):
**self._data **self._data
) )
for rk, profile in zip(self._new_rks, self._reach.profiles): self._reach.update_rks(self._new_rks)
profile.rk = rk
for begin_rk, end_rk, friction in zip(self._new_begin_rk, for begin_rk, end_rk, friction in zip(self._new_begin_rk,
self._new_end_rk, self._new_end_rk,
self._frictions): self._frictions):
friction.begin_rk = begin_rk friction.update_rks(begin_rk, end_rk)
friction.end_rk = end_rk
class MeshingCommand(QUndoCommand): class MeshingCommand(QUndoCommand):
@@ -411,13 +409,13 @@ class ShiftCommand(QUndoCommand):
def undo(self): def undo(self):
for i in self._rows: for i in self._rows:
profile = self._reach.profiles[i] profile = self._reach.profiles[i]
self._reach.profiles[i].shift( profile.shift(
-self._dx, -self._dy, -self._dz -self._dx, -self._dy, -self._dz
) )
def redo(self): def redo(self):
for i in self._rows: for i in self._rows:
profile = self._reach.profiles[i] profile = self._reach.profiles[i]
self._reach.profiles[i].shift( profile.shift(
self._dx, self._dy, self._dz self._dx, self._dy, self._dz
) )
+59 -6
View File
@@ -20,6 +20,7 @@ import os
import sys import sys
import time import time
import pathlib import pathlib
from pathlib import Path
import logging import logging
from copy import deepcopy from copy import deepcopy
@@ -301,7 +302,6 @@ class GeometryWindow(PamhyrWindow):
self.tableView.model().blockSignals(False) self.tableView.model().blockSignals(False)
def edit_meshing(self): def edit_meshing(self):
rows = list( rows = list(
set( set(
(i.row() for i in self.tableView.selectedIndexes()) (i.row() for i in self.tableView.selectedIndexes())
@@ -325,6 +325,25 @@ class GeometryWindow(PamhyrWindow):
logger_exception(e) logger_exception(e)
return return
ind = []
for i in range(self._reach.number_profiles):
if self._reach.profile(i).rk in selected_rk:
ind.append(i)
self.tableView.setFocus()
selection = self.tableView.selectionModel()
index = QItemSelection()
if len(ind) > 0:
for i in ind:
index.append(QItemSelectionRange(
self.tableView.model().index(i, 0))
)
selection.select(
index,
QItemSelectionModel.Rows |
QItemSelectionModel.ClearAndSelect |
QItemSelectionModel.Select
)
def _edit_meshing(self, data): def _edit_meshing(self, data):
try: try:
mesher = InternalMeshing() mesher = InternalMeshing()
@@ -352,6 +371,29 @@ class GeometryWindow(PamhyrWindow):
parent=self parent=self
) )
if dlg.exec(): if dlg.exec():
empty_profiles = [
profile for profile in self._reach.profiles
if profile.number_points == 0
]
if empty_profiles:
profile_names = ", ".join(
profile.name or str(profile.num)
for profile in empty_profiles
)
self.message_box(
(
self._trad["update_rk_error"] +
"\n" +
self._trad["update_rk_empty_profiles"]
),
(
self._trad["update_rk_empty_profiles_info"] +
f" {profile_names}"
),
window_title=self._trad["warning"]
)
return
data = { data = {
"origin": dlg.origin, "origin": dlg.origin,
"directrices": [dlg.begin_dir, dlg.end_dir], "directrices": [dlg.begin_dir, dlg.end_dir],
@@ -370,9 +412,6 @@ class GeometryWindow(PamhyrWindow):
except Exception as e: except Exception as e:
logger_exception(e) logger_exception(e)
raise ExternFileMissingError( raise ExternFileMissingError(
module="mage",
filename="MailleurTT",
path=MeshingWithMageMailleurTT._path(),
src_except=e src_except=e
) )
@@ -685,10 +724,24 @@ class GeometryWindow(PamhyrWindow):
) )
if filename != '' and filename is not None: if filename != '' and filename is not None:
self._export_to_file_st(filename) suffix = Path(filename).suffix
if suffix != "":
if suffix == ".st" or suffix == ".ST":
self._export_to_file_st(filename[:-3])
else:
# Warning popup when the format is not exportable
win = QtWidgets.QMessageBox()
win.setIcon(QtWidgets.QMessageBox.Warning)
win.setWindowTitle(self._trad["warning"])
win.setText(self._trad["format_not_exportable"])
win.exec()
pass
else:
self._export_to_file_st(filename)
def _export_to_file_st(self, filename): def _export_to_file_st(self, filename):
with open(filename, "w+") as f: with open(filename+".st", "w+") as f:
f.write("# Exported from Pamhyr2\n") f.write("# Exported from Pamhyr2\n")
self._export_to_file_st_reach(f, self._reach) self._export_to_file_st_reach(f, self._reach)
@@ -138,6 +138,9 @@ class TableModel(PamhyrTableModel):
if not index.isValid() or role != Qt.EditRole: if not index.isValid() or role != Qt.EditRole:
return False return False
if self.is_same_data(index, value):
return False
row = index.row() row = index.row()
column = index.column() column = index.column()
@@ -288,6 +291,9 @@ class ParametersTableModel(PamhyrTableModel):
if not index.isValid() or role != Qt.EditRole: if not index.isValid() or role != Qt.EditRole:
return False return False
if self.is_same_data(index, value):
return False
if self._hs_index is None: if self._hs_index is None:
return QVariant() return QVariant()
+1 -1
View File
@@ -42,7 +42,7 @@ class PlotAC(PamhyrPlot):
self._isometric_axis = False self._isometric_axis = False
self._auto_relim_update = True self._auto_relim_update = True
self._autoscale_update = True self._autoscale_update = False
@property @property
def river(self): def river(self):
+12 -6
View File
@@ -50,7 +50,7 @@ class PlotRKC(PamhyrPlot):
self._isometric_axis = False self._isometric_axis = False
self._auto_relim_update = True self._auto_relim_update = True
self._autoscale_update = True self._autoscale_update = False
self.parent = parent self.parent = parent
self.anotate_lst = [] self.anotate_lst = []
@@ -136,13 +136,18 @@ class PlotRKC(PamhyrPlot):
hs_color = [] hs_color = []
index = self.parent.tableView.selectedIndexes() index = self.parent.tableView.selectedIndexes()
for i, hs in enumerate(lhs): for i, hs in enumerate(lhs):
section = hs.input_section
if section is None:
continue
if i == index[0].row(): if i == index[0].row():
hs_color.append("blue") color = "blue"
elif hs.enabled: elif hs.enabled:
hs_color.append("red") color = "red"
else: else:
hs_color.append("darkgrey") color = "darkgrey"
x = hs.input_section.rk
x = section.rk
if x is not None: if x is not None:
a = self.canvas.axes.annotate( a = self.canvas.axes.annotate(
" > " + hs.name, " > " + hs.name,
@@ -151,12 +156,13 @@ class PlotRKC(PamhyrPlot):
verticalalignment='top', verticalalignment='top',
annotation_clip=True, annotation_clip=True,
fontsize=9, fontsize=9,
color=hs_color[-1], color=color,
) )
self.anotate_lst.append(a) self.anotate_lst.append(a)
vx.append(x) vx.append(x)
vymin.append(min(z_min)) vymin.append(min(z_min))
vymax.append(max(z_max)) vymax.append(max(z_max))
hs_color.append(color)
self.hs_vlines = self.canvas.axes.vlines( self.hs_vlines = self.canvas.axes.vlines(
x=vx, ymin=vymin, ymax=vymax, x=vx, ymin=vymin, ymax=vymax,
+3
View File
@@ -169,6 +169,9 @@ class TableModel(PamhyrTableModel):
if not index.isValid() or role != Qt.EditRole: if not index.isValid() or role != Qt.EditRole:
return False return False
if self.is_same_data(index, value):
return False
row = index.row() row = index.row()
column = index.column() column = index.column()
na = self._trad['not_associated'] na = self._trad['not_associated']
+1 -1
View File
@@ -47,7 +47,7 @@ class PlotDRK(PamhyrPlot):
self._isometric_axis = False self._isometric_axis = False
self._auto_relim_update = True self._auto_relim_update = True
self._autoscale_update = True self._autoscale_update = False
@timer @timer
def draw(self, highlight=None): def draw(self, highlight=None):
+1 -1
View File
@@ -37,7 +37,7 @@ class PlotDischarge(PamhyrPlot):
self._isometric_axis = False self._isometric_axis = False
self._auto_relim_update = True self._auto_relim_update = True
self._autoscale_update = True self._autoscale_update = False
@timer @timer
def draw(self): def draw(self):
+4 -1
View File
@@ -153,6 +153,9 @@ class InitialConditionTableModel(PamhyrTableModel):
if not index.isValid() or role != Qt.EditRole: if not index.isValid() or role != Qt.EditRole:
return False return False
if self.is_same_data(index, value):
return False
row = index.row() row = index.row()
column = index.column() column = index.column()
@@ -173,7 +176,7 @@ class InitialConditionTableModel(PamhyrTableModel):
def add(self, row, parent=QModelIndex()): def add(self, row, parent=QModelIndex()):
self.beginInsertRows(parent, row, row - 1) self.beginInsertRows(parent, row, row - 1)
row = self.get_true_data_row(row) # row = self.get_true_data_row(row)
self._undo.push( self._undo.push(
AddCommand( AddCommand(
-2
View File
@@ -107,8 +107,6 @@ class InitialConditionsWindow(PamhyrWindow):
self.setup_connections() self.setup_connections()
self.setub_dialogs() self.setub_dialogs()
self.ui.setWindowTitle(self._title)
def setup_table(self): def setup_table(self):
table = self.find(QTableView, f"tableView") table = self.find(QTableView, f"tableView")
self._delegate_rk = ComboBoxDelegate( self._delegate_rk = ComboBoxDelegate(
+19 -2
View File
@@ -117,7 +117,12 @@ class InitialConditionTableModel(PamhyrTableModel):
self._data = data self._data = data
def _setup_lst(self): def _setup_lst(self):
self._lst = self._data._data self._lst = list(
filter(
lambda ica: ica._deleted is False,
self._data._data
)
)
def rowCount(self, parent): def rowCount(self, parent):
return len(self._lst) return len(self._lst)
@@ -183,6 +188,9 @@ class InitialConditionTableModel(PamhyrTableModel):
if not index.isValid() or role != Qt.EditRole: if not index.isValid() or role != Qt.EditRole:
return False return False
if self.is_same_data(index, value):
return False
row = index.row() row = index.row()
column = index.column() column = index.column()
@@ -216,25 +224,34 @@ class InitialConditionTableModel(PamhyrTableModel):
) )
) )
self._setup_lst()
self.endInsertRows() self.endInsertRows()
self.layoutChanged.emit() self.layoutChanged.emit()
def delete(self, rows, parent=QModelIndex()): def delete(self, rows, parent=QModelIndex()):
self.beginRemoveRows(parent, rows[0], rows[-1]) self.beginRemoveRows(parent, rows[0], rows[-1])
data_rows = {
id(ica): i for i, ica in enumerate(self._data._data)
}
self._undo.push( self._undo.push(
DelCommand( DelCommand(
self._data, self._lst, rows self._data,
self._data._data,
[data_rows[id(self._lst[row])] for row in rows]
) )
) )
self._setup_lst()
self.endRemoveRows() self.endRemoveRows()
self.layoutChanged.emit() self.layoutChanged.emit()
def undo(self): def undo(self):
self._undo.undo() self._undo.undo()
self._setup_lst()
self.layoutChanged.emit() self.layoutChanged.emit()
def redo(self): def redo(self):
self._undo.redo() self._undo.redo()
self._setup_lst()
self.layoutChanged.emit() self.layoutChanged.emit()
@@ -181,7 +181,6 @@ class DelCommand(QUndoCommand):
self._data = data self._data = data
self._ics_spec = ics_spec self._ics_spec = ics_spec
self._rows = rows self._rows = rows
# self._data = data
self._ic = [] self._ic = []
for row in rows: for row in rows:
+45 -12
View File
@@ -68,15 +68,22 @@ class InitialConditionsAdisTSWindow(PamhyrWindow):
_pamhyr_ui = "InitialConditionsAdisTS" _pamhyr_ui = "InitialConditionsAdisTS"
_pamhyr_name = "Initial condition AdisTS" _pamhyr_name = "Initial condition AdisTS"
def __init__(self, data=None, study=None, config=None, parent=None): def __init__(self, data=None, pollutant_id=None, study=None,
config=None, parent=None):
self._data = [] self._data = []
self._data.append(data) self._data.append(data)
self._pollutant_id = pollutant_id
trad = IcAdisTSTranslate() trad = IcAdisTSTranslate()
self._pollutant_name = next(
(x.name for x in study._river._Pollutants.Pollutants_List
if x.id == self._pollutant_id),
None
)
name = ( name = (
trad[self._pamhyr_name] + trad[self._pamhyr_name] +
" - " + study.name + " - " + study.name +
" - " + self._data[0].name " - " + self._pollutant_name
) )
super(InitialConditionsAdisTSWindow, self).__init__( super(InitialConditionsAdisTSWindow, self).__init__(
@@ -93,18 +100,21 @@ class InitialConditionsAdisTSWindow(PamhyrWindow):
self.setup_table() self.setup_table()
self.ui.setWindowTitle(self._title)
def setup_table(self): def setup_table(self):
path_icons = os.path.join(self._get_ui_directory(), f"ressources") path_icons = os.path.join(self._get_ui_directory(), f"ressources")
table_default = self.find(QTableView, f"tableView") table_default = self.find(QTableView, f"tableView")
if self._study.is_editable():
editable_headers = self._trad.get_dict("table_headers")
else:
editable_headers = []
self._table = InitialConditionTableDefaultModel( self._table = InitialConditionTableDefaultModel(
table_view=table_default, table_view=table_default,
table_headers=self._trad.get_dict("table_headers"), table_headers=self._trad.get_dict("table_headers"),
editable_headers=["name", "concentration", "eg", "em", "ed"], editable_headers=editable_headers,
delegates={}, delegates={},
data=self._data, data=self._data,
undo=self._undo_stack, undo=self._undo_stack,
@@ -124,10 +134,14 @@ class InitialConditionsAdisTSWindow(PamhyrWindow):
action_add = QAction(self) action_add = QAction(self)
action_add.setIcon(QIcon(os.path.join(path_icons, f"add.png"))) action_add.setIcon(QIcon(os.path.join(path_icons, f"add.png")))
action_add.triggered.connect(self.add)
if self._study.is_editable():
action_add.triggered.connect(self.add)
action_delete = QAction(self) action_delete = QAction(self)
action_delete.setIcon(QIcon(os.path.join(path_icons, f"del.png"))) action_delete.setIcon(QIcon(os.path.join(path_icons, f"del.png")))
action_delete.triggered.connect(self.delete)
if self._study.is_editable():
action_delete.triggered.connect(self.delete)
toolBar.addAction(action_add) toolBar.addAction(action_add)
toolBar.addAction(action_delete) toolBar.addAction(action_delete)
@@ -150,12 +164,15 @@ class InitialConditionsAdisTSWindow(PamhyrWindow):
mode="rk" mode="rk"
) )
if self._study.is_editable():
editable_headers_spec = self._trad.get_dict("table_headers_spec")
else:
editable_headers_spec = []
self._table_spec = InitialConditionTableModel( self._table_spec = InitialConditionTableModel(
table_view=self.table_spec, table_view=self.table_spec,
table_headers=self._trad.get_dict("table_headers_spec"), table_headers=self._trad.get_dict("table_headers_spec"),
editable_headers=["name", "reach", "start_rk", editable_headers=editable_headers_spec,
"end_rk", "concentration",
"eg", "em", "ed", "rate"],
delegates={ delegates={
"reach": self._delegate_reach, "reach": self._delegate_reach,
"start_rk": self._delegate_rk, "start_rk": self._delegate_rk,
@@ -266,11 +283,27 @@ class InitialConditionsAdisTSWindow(PamhyrWindow):
self._update() self._update()
def _undo(self): def _undo(self):
self._table.undo() undo_stack = self._undo_stack
if undo_stack is None or not undo_stack.canUndo():
return
if isinstance(undo_stack.command(undo_stack.index() - 1), SetCommand):
self._table.undo()
else:
self._table_spec.undo()
self._update() self._update()
def _redo(self): def _redo(self):
self._table.redo() undo_stack = self._undo_stack
if undo_stack is None or not undo_stack.canRedo():
return
if isinstance(undo_stack.command(undo_stack.index()), SetCommand):
self._table.redo()
else:
self._table_spec.redo()
self._update() self._update()
def add(self): def add(self):
+1 -1
View File
@@ -54,7 +54,7 @@ class Plot(PamhyrPlot):
self._isometric_axis = False self._isometric_axis = False
self._auto_relim_update = True self._auto_relim_update = True
self._autoscale_update = True self._autoscale_update = False
def custom_ticks(self): def custom_ticks(self):
if self.data.header[0] != "time": if self.data.header[0] != "time":
@@ -86,6 +86,9 @@ class TableModel(PamhyrTableModel):
if not index.isValid() or role != Qt.EditRole: if not index.isValid() or role != Qt.EditRole:
return False return False
if self.is_same_data(index, value):
return False
row = index.row() row = index.row()
column = index.column() column = index.column()
+16 -3
View File
@@ -127,7 +127,7 @@ class ComboBoxDelegate(QItemDelegate):
) )
) )
value = profiles[0].rk if len(profiles) > 0 else None value = profiles[0].pamhyr_id if len(profiles) > 0 else None
else: else:
value = text value = text
@@ -197,6 +197,9 @@ class TableModel(PamhyrTableModel):
if not index.isValid() or role != Qt.EditRole: if not index.isValid() or role != Qt.EditRole:
return False return False
if self.is_same_data(index, value):
return False
row = index.row() row = index.row()
column = index.column() column = index.column()
@@ -222,15 +225,25 @@ class TableModel(PamhyrTableModel):
) )
) )
elif self._headers[column] == "begin_rk": elif self._headers[column] == "begin_rk":
_edge = self._lst.get(self._tab, row).reach
_begin_rk = next(
p for p in _edge.reach.profiles
if p.pamhyr_id == value
)
self._undo.push( self._undo.push(
SetBeginCommand( SetBeginCommand(
self._lst, self._tab, row, value self._lst, self._tab, row, _begin_rk
) )
) )
elif self._headers[column] == "end_rk": elif self._headers[column] == "end_rk":
_edge = self._lst.get(self._tab, row).reach
_end_rk = next(
p for p in _edge.reach.profiles
if p.pamhyr_id == value
)
self._undo.push( self._undo.push(
SetEndCommand( SetEndCommand(
self._lst, self._tab, row, value self._lst, self._tab, row, _end_rk
) )
) )
except Exception as e: except Exception as e:
+1 -1
View File
@@ -156,7 +156,7 @@ class DelCommand(QUndoCommand):
el.set_as_not_deleted() el.set_as_not_deleted()
def redo(self): def redo(self):
for el in self._bc: for el in self._lc:
el.set_as_deleted() el.set_as_deleted()
+2 -1
View File
@@ -53,7 +53,8 @@ class LCTranslate(MainTranslate):
self._dict["y"] = _translate("Geometry", "Y (m)") self._dict["y"] = _translate("Geometry", "Y (m)")
self._dict["z"] = _translate("Geometry", "Z (m)") self._dict["z"] = _translate("Geometry", "Z (m)")
self._dict["file_lat"] = _translate( self._dict["file_lat"] = _translate(
"LateralContribution", "Shapefile (*.LAT *.lat)") "LateralContribution",
"Mage lateral contributions file (*.LAT *.lat)")
self._dict["file_all"] = _translate( self._dict["file_all"] = _translate(
"LateralContribution", "All files (*)") "LateralContribution", "All files (*)")
@@ -54,7 +54,7 @@ class Plot(PamhyrPlot):
self._isometric_axis = False self._isometric_axis = False
self._auto_relim_update = True self._auto_relim_update = True
self._autoscale_update = True self._autoscale_update = False
def custom_ticks(self): def custom_ticks(self):
if self.data.header[0] != "time": if self.data.header[0] != "time":
@@ -52,6 +52,23 @@ logger = logging.getLogger()
class TableModel(PamhyrTableModel): class TableModel(PamhyrTableModel):
def get_true_data_row(self, row):
if len(self._data.data) > 0:
lc = self._data.data[row]
else:
return 0
return next(
map(
lambda e: e[0],
filter(
lambda e: e[1] == lc,
enumerate(self._data._data)
)
), 0
)
def data(self, index, role): def data(self, index, role):
if role == Qt.TextAlignmentRole: if role == Qt.TextAlignmentRole:
return Qt.AlignHCenter | Qt.AlignVCenter return Qt.AlignHCenter | Qt.AlignVCenter
@@ -65,6 +82,7 @@ class TableModel(PamhyrTableModel):
value = QVariant() value = QVariant()
if 0 <= column < 2: if 0 <= column < 2:
row = self.get_true_data_row(row)
v = self._data._data[row][column] v = self._data._data[row][column]
if self._data._types[column] == float: if self._data._types[column] == float:
value = f"{v:.4f}" value = f"{v:.4f}"
@@ -82,10 +100,15 @@ class TableModel(PamhyrTableModel):
if not index.isValid() or role != Qt.EditRole: if not index.isValid() or role != Qt.EditRole:
return False return False
if self.is_same_data(index, value):
return False
row = index.row() row = index.row()
column = index.column() column = index.column()
try: try:
row = self.get_true_data_row(row)
self._undo.push( self._undo.push(
SetDataCommand( SetDataCommand(
self._data, row, column, value self._data, row, column, value
@@ -113,10 +136,18 @@ class TableModel(PamhyrTableModel):
def delete(self, rows, parent=QModelIndex()): def delete(self, rows, parent=QModelIndex()):
self.beginRemoveRows(parent, rows[0], rows[-1]) self.beginRemoveRows(parent, rows[0], rows[-1])
_rows = list(
map(
lambda r: self.get_true_data_row(r),
rows
)
)
self._undo.push( self._undo.push(
DelCommand( DelCommand(
self._data, rows self._data, _rows
) )
) )
self.endRemoveRows() self.endRemoveRows()
self.layoutChanged.emit()
@@ -34,29 +34,14 @@ class SetDataCommand(QUndoCommand):
self._data = data self._data = data
self._index = index self._index = index
self._column = column self._column = column
self._old = self._data._data[self._index][self._column] self._old = self._data.get_i(self._index)[self._column]
_type = self._data._types[self._column] self._new = new_value
self._new = _type(new_value)
def undo(self): def undo(self):
if self._column == 0: self._data._set_i_c_v(self._index, self._column, self._old)
self._data._data[self._index] = (
self._old, self._data._data[self._index][1]
)
else:
self._data._data[self._index] = (
self._data._data[self._index][0], self._old
)
def redo(self): def redo(self):
if self._column == 0: self._data._set_i_c_v(self._index, self._column, self._new)
self._data._data[self._index] = (
self._new, self._data._data[self._index][1]
)
else:
self._data._data[self._index] = (
self._data._data[self._index][0], self._new
)
class AddCommand(QUndoCommand): class AddCommand(QUndoCommand):
@@ -72,11 +57,7 @@ class AddCommand(QUndoCommand):
def redo(self): def redo(self):
if self._new is None: if self._new is None:
self._new = self._data._data.insert( self._new = self._data.add(self._index)
self._index, (
self._data._types[0](0), self._data._types[1](0.0)
)
)
else: else:
self._data._data.insert(self._index, self._new) self._data._data.insert(self._index, self._new)
@@ -95,8 +76,8 @@ class DelCommand(QUndoCommand):
def undo(self): def undo(self):
for row, el in self._lc: for row, el in self._lc:
self._data._data.insert(row, el) el.set_as_not_deleted()
def redo(self): def redo(self):
for row in self._rows: for row, el in self._lc:
del self._data._data[row] el.set_as_deleted()
@@ -50,14 +50,38 @@ class EditLateralContributionAdisTSWindow(PamhyrWindow):
_pamhyr_ui = "EditLateralContributionAdisTS" _pamhyr_ui = "EditLateralContributionAdisTS"
_pamhyr_name = "Edit lateral contribution AdisTS" _pamhyr_name = "Edit lateral contribution AdisTS"
def __init__(self, data=None, def __init__(self, data=None, study=None, config=None, parent=None):
study=None, config=None,
parent=None):
self._data = data self._data = data
trad = LCETranslate() trad = LCETranslate()
name = trad[self._pamhyr_name] name = trad[self._pamhyr_name]
if self._data is not None:
if self._data.reach is not None:
reach_name = next(filter(
lambda reach: reach.id == self._data.reach,
study.river.reachs()
)).name
else:
reach_name = trad['not_associated']
if self._data.begin_rk is not None:
begin_rk = self._data.begin_rk
else:
begin_rk = trad['not_associated']
if self._data.end_rk is not None:
end_rk = self._data.end_rk
else:
end_rk = trad['not_associated']
name += (
f" - {study.name} - " +
f"{reach_name} - " +
f"({begin_rk} - " +
f"{end_rk})"
)
super(EditLateralContributionAdisTSWindow, self).__init__( super(EditLateralContributionAdisTSWindow, self).__init__(
title=name, title=name,
study=study, study=study,
@@ -66,20 +90,6 @@ class EditLateralContributionAdisTSWindow(PamhyrWindow):
parent=parent parent=parent
) )
if self._data is not None:
if self._data.edge is not None:
edge_name = next(filter(
lambda edge: edge.id == self._data.edge,
self._study.river.edges()
)).name
else:
edge_name = trad['not_associated']
name += (
f"{study.name} - " +
f"{edge_name})"
)
self._hash_data.append(data) self._hash_data.append(data)
self.setup_table() self.setup_table()
@@ -93,6 +103,11 @@ class EditLateralContributionAdisTSWindow(PamhyrWindow):
parent=self parent=self
) )
if self._study.is_editable():
editable_headers = self._trad.get_dict("table_headers")
else:
editable_headers = []
headers = {} headers = {}
table_headers = self._trad.get_dict("table_headers") table_headers = self._trad.get_dict("table_headers")
for h in self._data.header: for h in self._data.header:
@@ -102,7 +117,7 @@ class EditLateralContributionAdisTSWindow(PamhyrWindow):
self._table = TableModel( self._table = TableModel(
table_view=table, table_view=table,
table_headers=headers, table_headers=headers,
editable_headers=self._data.header, editable_headers=editable_headers,
delegates={ delegates={
# "time": self._delegate_time, # "time": self._delegate_time,
}, },
@@ -137,8 +152,9 @@ class EditLateralContributionAdisTSWindow(PamhyrWindow):
self.plot.draw() self.plot.draw()
def setup_connections(self): def setup_connections(self):
self.find(QAction, "action_add").triggered.connect(self.add) if self._study.is_editable():
self.find(QAction, "action_del").triggered.connect(self.delete) self.find(QAction, "action_add").triggered.connect(self.add)
self.find(QAction, "action_del").triggered.connect(self.delete)
self._table.dataChanged.connect(self.update) self._table.dataChanged.connect(self.update)
+38 -3
View File
@@ -116,10 +116,22 @@ class TableModel(PamhyrTableModel):
self._pollutant = pollutant self._pollutant = pollutant
super(TableModel, self).__init__(trad=trad, **kwargs) super(TableModel, self).__init__(trad=trad, **kwargs)
self._setup_lst()
def _setup_lst(self): def _setup_lst(self):
self._lst = self._data.lateral_contributions_adists.lst if self._lcs_list is not None:
self._tab = self._opt_data self._lcs_pol_list = [
lcs for lcs in self._lcs_list._lst
if lcs.pollutant == self._pollutant
]
self._lst = list(
filter(
lambda x: x._deleted is False,
self._lcs_pol_list
)
)
# self._tab = self._opt_data
self._long_types = self._trad.get_dict("long_types") self._long_types = self._trad.get_dict("long_types")
def rowCount(self, parent): def rowCount(self, parent):
@@ -150,6 +162,9 @@ class TableModel(PamhyrTableModel):
if not index.isValid() or role != Qt.EditRole: if not index.isValid() or role != Qt.EditRole:
return False return False
if self.is_same_data(index, value):
return False
row = index.row() row = index.row()
column = index.column() column = index.column()
@@ -190,17 +205,37 @@ class TableModel(PamhyrTableModel):
) )
) )
self._setup_lst()
self.endInsertRows() self.endInsertRows()
self.layoutChanged.emit() self.layoutChanged.emit()
def delete(self, rows, parent=QModelIndex()): def delete(self, rows, parent=QModelIndex()):
self.beginRemoveRows(parent, rows[0], rows[-1]) self.beginRemoveRows(parent, rows[0], rows[-1])
row_by_lc = {
id(lc): row for row, lc in enumerate(self._lcs_list._lst)
}
global_rows = [
row_by_lc[id(self._lst[row])]
for row in rows
if 0 <= row < len(self._lst)
]
self._undo.push( self._undo.push(
DelCommand( DelCommand(
self._lst, rows self._lcs_list, global_rows
) )
) )
self._setup_lst()
self.endRemoveRows() self.endRemoveRows()
self.layoutChanged.emit() self.layoutChanged.emit()
def undo(self):
self._undo.undo()
self._setup_lst()
self.layoutChanged.emit()
def redo(self):
self._undo.redo()
self._setup_lst()
self.layoutChanged.emit()
@@ -91,13 +91,13 @@ class AddCommand(QUndoCommand):
self._new = None self._new = None
def undo(self): def undo(self):
del self._lcs[self._index] self._lcs.delete_i([self._index])
def redo(self): def redo(self):
if self._new is None: if self._new is None:
self._new = self._lcs.new(self._index, self._pollutant) self._new = self._lcs.new(self._index, self._pollutant)
else: else:
self._lcs_lst.insert(self._index, self._new) self._lcs.insert(self._index, self._new)
class DelCommand(QUndoCommand): class DelCommand(QUndoCommand):
@@ -107,15 +107,14 @@ class DelCommand(QUndoCommand):
self._lcs = lcs self._lcs = lcs
self._rows = rows self._rows = rows
self._bc = [] self._lc = []
for row in rows: for row in rows:
self._bc.append((row, self._lcs[row])) self._lc.append((row, self._lcs._lst[row]))
self._bc.sort() self._lc.sort()
def undo(self): def undo(self):
for row, el in self._bc: for row, el in self._lc:
self._lcs.insert(row, el) self._lcs.insert(row, el)
def redo(self): def redo(self):
for row in self._rows: self._lcs.delete_i(self._rows)
del self._lcs[row]
+30 -8
View File
@@ -57,11 +57,24 @@ class LateralContributionAdisTSWindow(PamhyrWindow):
_pamhyr_ui = "LateralContributionsAdisTS" _pamhyr_ui = "LateralContributionsAdisTS"
_pamhyr_name = "Lateral contribution AdisTS" _pamhyr_name = "Lateral contribution AdisTS"
def __init__(self, study=None, pollutant=None, config=None, parent=None): def __init__(self, data=None, study=None, pollutant_id=None,
config=None, parent=None):
self._pollutant = pollutant_id
self._study = study
self._data = data
trad = LCTranslate() trad = LCTranslate()
name = trad[self._pamhyr_name] + " - " + study.name
self._pollutant = pollutant self._pollutant_name = next(
(x.name for x in study._river._Pollutants.Pollutants_List
if x.id == self._pollutant),
None
)
name = (
trad[self._pamhyr_name] +
" - " + study.name +
" - " + self._pollutant_name
)
super(LateralContributionAdisTSWindow, self).__init__( super(LateralContributionAdisTSWindow, self).__init__(
title=name, title=name,
@@ -79,9 +92,13 @@ class LateralContributionAdisTSWindow(PamhyrWindow):
def setup_table(self): def setup_table(self):
self._table = {} self._table = {}
self._delegate_rk = [] self._delegate_rk = []
if self._study.is_editable():
editable_headers = self._trad.get_dict("table_headers")
else:
editable_headers = []
delegate_rk = ComboBoxDelegate( delegate_rk = ComboBoxDelegate(
data=None, data=None,
mode="rk", mode="rk",
@@ -101,7 +118,7 @@ class LateralContributionAdisTSWindow(PamhyrWindow):
self._table = TableModel( self._table = TableModel(
table_view=table, table_view=table,
table_headers=self._trad.get_dict("table_headers"), table_headers=self._trad.get_dict("table_headers"),
editable_headers=self._trad.get_dict("table_headers"), editable_headers=editable_headers,
delegates={ delegates={
"reach": self._delegate_reach, "reach": self._delegate_reach,
"begin_rk": delegate_rk, "begin_rk": delegate_rk,
@@ -110,7 +127,7 @@ class LateralContributionAdisTSWindow(PamhyrWindow):
data=self._study.river, data=self._study.river,
undo=self._undo_stack, undo=self._undo_stack,
trad=self._trad, trad=self._trad,
opt_data="liquid", # opt_data="liquid",
pollutant=self._pollutant, pollutant=self._pollutant,
lcs_list=self._lcs, lcs_list=self._lcs,
) )
@@ -133,8 +150,10 @@ class LateralContributionAdisTSWindow(PamhyrWindow):
) )
def setup_connections(self): def setup_connections(self):
self.find(QAction, "action_add").triggered.connect(self.add) if self._study.is_editable():
self.find(QAction, "action_del").triggered.connect(self.delete) self.find(QAction, "action_add").triggered.connect(self.add)
self.find(QAction, "action_del").triggered.connect(self.delete)
self.find(QAction, "action_edit").triggered.connect(self.edit) self.find(QAction, "action_edit").triggered.connect(self.edit)
table = self.find(QTableView, f"tableView") table = self.find(QTableView, f"tableView")
@@ -237,6 +256,9 @@ class LateralContributionAdisTSWindow(PamhyrWindow):
def edit(self): def edit(self):
rows = self.index_selected_rows() rows = self.index_selected_rows()
if not rows:
return
for row in rows: for row in rows:
data = self._lcs.lst[row] data = self._lcs.lst[row]
+81 -24
View File
@@ -299,6 +299,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
"action_menu_rep_additional_lines": self.open_rep_lines, "action_menu_rep_additional_lines": self.open_rep_lines,
"action_menu_edit_geotiff": self.open_geotiff, "action_menu_edit_geotiff": self.open_geotiff,
"action_menu_close": self.close_model, "action_menu_close": self.close_model,
"action_menu_quit": self.close,
"action_menu_results_last": self.open_last_results, "action_menu_results_last": self.open_last_results,
"action_menu_open_results_from_file": self.open_results_from_file, "action_menu_open_results_from_file": self.open_results_from_file,
"action_menu_compare_scenarios_results": "action_menu_compare_scenarios_results":
@@ -951,6 +952,13 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
elif res == QMessageBox.Cancel: elif res == QMessageBox.Cancel:
return False return False
def msg_results_warning(self, text="", info=""):
self.message_box(
window_title=self._trad["Warning"],
text=text,
informative_text=info
)
######################### #########################
# SUB WINDOWS MENU LIST # # SUB WINDOWS MENU LIST #
######################### #########################
@@ -1888,7 +1896,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
s3_name = solver1.name + " <> " + solver2.name s3_name = solver1.name + " <> " + solver2.name
if not (scenario2 is None or scenario2 is None): if not (scenario1 is None or scenario2 is None):
study1 = self._study.copy_from_scenario(scenario1) study1 = self._study.copy_from_scenario(scenario1)
study2 = self._study.copy_from_scenario(scenario2) study2 = self._study.copy_from_scenario(scenario2)
@@ -1925,7 +1933,6 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
@timer @timer
def _diff_results(self, solver1, solver2, solver3, result1, result2): def _diff_results(self, solver1, solver2, solver3, result1, result2):
result3 = Results(study=self._study, solver=solver3)
ts = sorted( ts = sorted(
list( list(
result1.get("timestamps").intersection( result1.get("timestamps").intersection(
@@ -1934,37 +1941,80 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
) )
) )
result3 = Results(study=self._study, solver=solver3)
result4 = Results(study=result1.study, solver=solver1)
result4._river = result1._river
result5 = Results(study=result2.study, solver=solver2)
result5._river = result2._river
result3.set("nb_reach", result1.get("nb_reach")) result3.set("nb_reach", result1.get("nb_reach"))
result4.set("nb_reach", result1.get("nb_reach"))
result5.set("nb_reach", result1.get("nb_reach"))
result3.set("nb_profile", result1.get("nb_profile")) result3.set("nb_profile", result1.get("nb_profile"))
result4.set("nb_profile", result1.get("nb_profile"))
result5.set("nb_profile", result1.get("nb_profile"))
result3.set("timestamps", ts) result3.set("timestamps", ts)
result4.set("timestamps", ts)
result5.set("timestamps", ts)
for i in range(int(result1.get("nb_reach"))): for i in range(int(result1.get("nb_reach"))):
r = result3.river.add(i) r = result3.river.add(i)
for timestamp in result3.get("timestamps"): for timestamp in ts:
for r in range(int(result1.get("nb_reach"))): for r in range(int(result1.get("nb_reach"))):
reach1 = result1.river.reach(r)
reach2 = result2.river.reach(r)
reach3 = result3.river.reach(r) reach3 = result3.river.reach(r)
reach1 = result4.river.reach(r)
reach2 = result5.river.reach(r)
for profile1, profile2, profile3 in zip( for p, (profile1, profile2, profile3) in enumerate(zip(
reach1.profiles, reach1.profiles,
reach2.profiles, reach2.profiles,
reach3.profiles): reach3.profiles)):
for key in ["Z", "Q", "V"]: for key in ["Z", "Q", "V"]:
d1 = profile1.get_ts_key(timestamp, key) d1 = profile1.get_ts_key(timestamp, key)
d2 = profile2.get_ts_key(timestamp, key) d2 = profile2.get_ts_key(timestamp, key)
d = d1 - d2 d = d1 - d2
reach3.set(p, timestamp, key, d)
reach1.set(p, timestamp, key, d1)
reach2.set(p, timestamp, key, d2)
if reach1.has_bedload():
d1 = profile1.get_ts_key(timestamp, 'zfd')
reach1.set(p, timestamp, 'zfd', d1)
if reach2.has_bedload():
d2 = profile2.get_ts_key(timestamp, 'zfd')
reach2.set(p, timestamp, 'zfd', d2)
if reach1.has_bedload():
d3 = d1 - d2
reach3.set(p, timestamp, 'zfd', d3)
profile3.set(timestamp, key, d) limits = reach3.profile(p).geometry.get_water_limits(
reach3.profile(p).get_ts_key(timestamp, "Z")
)
reach3.set(
p, timestamp,
"water_limits",
limits
)
limits = profile1.get_ts_key(timestamp, "water_limits")
reach1.set(p, timestamp, "water_limits", limits)
limits = profile2.get_ts_key(timestamp, "water_limits")
reach2.set(p, timestamp, "water_limits", limits)
limits = profile3.geometry\ for res in (result3, result4, result5):
.get_water_limits( for r in range(int(res.get("nb_reach"))):
profile3.get_ts_key(timestamp, "Z") for key in ["Z", "Q", "V"]:
) res.river.reach(r).bufferize(ts, key)
profile3.set(timestamp, "water_limits", limits) if res.river.reach(r).has_bedload():
res.river.reach(r).bufferize(ts, "zfd")
return [result1, result2, result3] for res in (result3, result4, result5):
for r in range(int(res.get("nb_reach"))):
for key in ["Z", "Q", "V"]:
res.river.reach(r).bufferize(ts, key)
if res.river.reach(r).has_bedload():
res.river.reach(r).bufferize(ts, "zfd")
return [result4, result5, result3]
def open_results_adists(self): def open_results_adists(self):
if self._study is None: if self._study is None:
@@ -1998,12 +2048,22 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
bin_list = list(filter(lambda s: "bin" in s, dir_list)) bin_list = list(filter(lambda s: "bin" in s, dir_list))
if len(bin_list) < 1: if len(bin_list) < 1:
# TODO message self.msg_results_warning(
info=self._trad["mb_results_not_found"],
text=self._trad["mb_results_bin_not_found"]
)
return return
path = os.path.normpath(dir_path) path = os.path.normpath(dir_path)
solver_name = path.split(os.sep)[-2] solver_name = path.split(os.sep)[-2]
solver_results = next(filter(lambda x: x.name == solver_name, solver_results = next(filter(lambda x: x.name == solver_name,
self.conf.solvers)) self.conf.solvers), None)
if solver_results is None:
self.msg_results_warning(
info=self._trad["mb_results_not_found"],
text=self._trad["mb_results_folder_not_found"]
)
return
solver_results_adists = solver_results.results( solver_results_adists = solver_results.results(
self._study, self._study,
repertory=dir_path, qlog=None) # self._output) repertory=dir_path, qlog=None) # self._output)
@@ -2015,13 +2075,10 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
results=solver_results_adists, results=solver_results_adists,
) )
else: else:
dlg = QDialog(self) self.msg_results_warning(
dlg.setWindowTitle("AdisTS Results") info=self._trad["mb_results_not_found"],
layout = QVBoxLayout() text=self._trad["mb_results_bin_not_found"]
message = QLabel("AdisTS Results not found") )
layout.addWidget(message)
dlg.setLayout(layout)
dlg.exec()
################# #################
# DOCUMENTATION # # DOCUMENTATION #
@@ -2065,7 +2122,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
def open_doc_user(self): def open_doc_user(self):
self.open_doc( self.open_doc(
"https://gitlab.irstea.fr/theophile.terraz/pamhyr/-/wikis/home" "https://gitlab.com/pamhyr/pamhyr2/-/wikis/home"
) )
def open_doc_dev(self, ext="pdf"): def open_doc_dev(self, ext="pdf"):
+54 -38
View File
@@ -901,7 +901,18 @@ class GraphWidget(QGraphicsView):
painter.drawRect(sceneRect) painter.drawRect(sceneRect)
def wheelEvent(self, event): def wheelEvent(self, event):
self.scaleView(math.pow(2.0, -event.angleDelta().y() / 240.0)) factor = math.pow(2.0, event.angleDelta().y() / 240.0)
old_pos = self.mapToScene(event.pos())
self.scaleView(factor)
new_pos = self.mapToScene(event.pos())
delta = old_pos - new_pos
# Compensation pour garder le point sous la souris fixe
self.translate(delta.x(), delta.y())
def scaleView(self, scaleFactor): def scaleView(self, scaleFactor):
factor = self.transform().scale( factor = self.transform().scale(
@@ -914,6 +925,11 @@ class GraphWidget(QGraphicsView):
self.scale(scaleFactor, scaleFactor) self.scale(scaleFactor, scaleFactor)
def mousePressEvent(self, event): def mousePressEvent(self, event):
if self._only_display:
return
locked = self.graph._status.is_read_only()
pos = self.mapToScene(event.pos()) pos = self.mapToScene(event.pos())
self.clicked = True self.clicked = True
@@ -922,9 +938,6 @@ class GraphWidget(QGraphicsView):
super(GraphWidget, self).mousePressEvent(event) super(GraphWidget, self).mousePressEvent(event)
return return
if self.graph._status.is_read_only():
return
# Move item and select edge item # Move item and select edge item
if self._state == "move": if self._state == "move":
self._selected_new_edge_src_node = None self._selected_new_edge_src_node = None
@@ -934,39 +947,42 @@ class GraphWidget(QGraphicsView):
edge = items[0] edge = items[0]
if edge: if edge:
self.set_current_edge(edge) self.set_current_edge(edge)
elif items and type(items[0]) is NodeItem: if not locked:
self._mouse_origin_x = pos.x() if items and type(items[0]) is NodeItem:
self._mouse_origin_y = pos.y() self._mouse_origin_x = pos.x()
self._current_moved_node = items[0] self._mouse_origin_y = pos.y()
self._current_moved_node = items[0]
# Add nodes and edges if not locked:
elif self._state == "add": # Add nodes and edges
items = self.items(event.pos()) if self._state == "add":
nodes = list(filter(lambda i: type(i) is NodeItem, items)) items = self.items(event.pos())
if not nodes: nodes = list(filter(lambda i: type(i) is NodeItem, items))
self.add_node(pos) if not nodes:
self.set_selected_new_edge_src_node(None) self.add_node(pos)
else: self.set_selected_new_edge_src_node(None)
if self.selected_new_edge_src_node() is None:
self.set_selected_new_edge_src_node(nodes[0])
else: else:
self.add_edge(self.selected_new_edge_src_node(), nodes[0]) if self.selected_new_edge_src_node() is None:
self.set_selected_new_edge_src_node(nodes[0])
else:
self.add_edge(self.selected_new_edge_src_node(),
nodes[0])
# Delete nodes and edges # Delete nodes and edges
elif self._state == "del": elif self._state == "del":
self._selected_new_edge_src_node = None self._selected_new_edge_src_node = None
items = list( items = list(
filter( filter(
lambda i: type(i) is NodeItem or type(i) is EdgeItem, lambda i: type(i) is NodeItem or type(i) is EdgeItem,
self.items(event.pos()) self.items(event.pos())
)
) )
) if len(items) > 0:
if len(items) > 0: item = items[0]
item = items[0] if type(item) is NodeItem:
if type(item) is NodeItem: self.del_node(item)
self.del_node(item) elif type(item) is EdgeItem:
elif type(item) is EdgeItem: self.del_edge(item)
self.del_edge(item)
self.update() self.update()
super(GraphWidget, self).mousePressEvent(event) super(GraphWidget, self).mousePressEvent(event)
@@ -974,10 +990,12 @@ class GraphWidget(QGraphicsView):
def mouseReleaseEvent(self, event): def mouseReleaseEvent(self, event):
self.clicked = False self.clicked = False
if self._only_display or self.graph._status.is_read_only(): if self._only_display:
return return
if self._state == "move": locked = self.graph._status.is_read_only()
if not locked and self._state == "move":
if self._current_moved_node is not None: if self._current_moved_node is not None:
pos = self.mapToScene(event.pos()) pos = self.mapToScene(event.pos())
self._undo.push( self._undo.push(
@@ -993,10 +1011,8 @@ class GraphWidget(QGraphicsView):
super(GraphWidget, self).mouseReleaseEvent(event) super(GraphWidget, self).mouseReleaseEvent(event)
def mouseMoveEvent(self, event): def mouseMoveEvent(self, event):
if self.graph._status.is_read_only():
return
pos = self.mapToScene(event.pos()) pos = self.mapToScene(event.pos())
locked = self.graph._status.is_read_only()
# Selecte item on the fly # Selecte item on the fly
items = self.items(event.pos()) items = self.items(event.pos())
+6
View File
@@ -105,6 +105,9 @@ class NodeTableModel(PamhyrTableModel):
return False return False
if role == Qt.EditRole: if role == Qt.EditRole:
if self.is_same_data(index, value):
return False
try: try:
self._undo.push( self._undo.push(
SetCommand( SetCommand(
@@ -155,6 +158,9 @@ class EdgeTableModel(PamhyrTableModel):
if role != Qt.EditRole: if role != Qt.EditRole:
return QVariant() return QVariant()
if self.is_same_data(index, value):
return False
try: try:
if (self._headers[index.column()] == "node1" or if (self._headers[index.column()] == "node1" or
self._headers[index.column()] == "node2"): self._headers[index.column()] == "node2"):
+1 -1
View File
@@ -147,7 +147,7 @@ class NetworkWindow(PamhyrWindow):
self._graph_widget.changeEdge.connect(self.update) self._graph_widget.changeEdge.connect(self.update)
self._graph_widget.changeNode.connect(self.update) self._graph_widget.changeNode.connect(self.update)
if self._study.is_editable: if self._study.is_editable():
self._nodes_model\ self._nodes_model\
.dataChanged\ .dataChanged\
.connect(self._graph_widget.rename_nodes) .connect(self._graph_widget.rename_nodes)
Loaded 100 of 151 files, more files were not shown because too many files have changed in this diff. Show more