Compare commits

...
52 Commits
Author SHA1 Message Date
Pierre-Antoine Rouby 4f91de4c97 Merge branch 'master' of gitlab-ssh.irstea.fr:theophile.terraz/pamhyr. 2023-11-29 14:49:02 +01:00
Pierre-Antoine Rouby 37b04961f1 IC: Add Manning/Strickler equation for IC approximation. 2023-11-29 14:47:35 +01:00
Theophile Terraz 0a63f16f62 Merge branch 'master' of gitlab-ssh.irstea.fr:theophile.terraz/pamhyr 2023-11-29 14:34:11 +01:00
Theophile Terraz 92eb729a39 add Reservoirs 2023-11-29 14:34:06 +01:00
Pierre-Antoine Rouby d891c9783d Model: Fix possible data removing durring save. 2023-11-27 16:58:46 +01:00
Pierre-Antoine Rouby 51858597cd Mage: ST export minor change. 2023-11-27 14:49:42 +01:00
Pierre-Antoine Rouby 8b3baf433f Solver run: Used merged channels to get stderr. 2023-11-27 14:31:04 +01:00
Pierre-Antoine Rouby 12e3d779c6 Mage: Fix BC export when node is not associate. 2023-11-27 11:40:59 +01:00
Pierre-Antoine Rouby c6274890a7 BC: Rename some BC types. 2023-11-27 11:29:39 +01:00
Theophile Terraz 907fc0c53a add step-by-step tutorial 2023-11-27 09:44:57 +01:00
Pierre-Antoine Rouby b4ee76416e BC, LC: Disable time delegate (fix #23). 2023-11-27 09:37:23 +01:00
Pierre-Antoine Rouby a88b42a8ef BC: Fix get_assoc_to_node in case of no results. 2023-11-27 09:21:23 +01:00
Pierre-Antoine Rouby 08f01aee69 Network: Fix #22. 2023-11-27 09:12:31 +01:00
Pierre-Antoine Rouby c69cbdb34e BC: Make node association unique (#25). 2023-11-27 08:55:38 +01:00
Pierre-Antoine Rouby 01fd7dbec8 Pamhyr2: Fix some type compare. 2023-11-22 15:47:57 +01:00
Pierre-Antoine Rouby 2af9b0018c SQL: Minor change. 2023-11-22 15:39:21 +01:00
Pierre-Antoine Rouby 7f8e8d5a9f SQL: Minor change and add exception handler. 2023-11-22 14:58:03 +01:00
Pierre-Antoine Rouby c9887a97e7 Friction: Disable name column because the value is not in database. 2023-11-22 10:20:50 +01:00
Pierre-Antoine Rouby 1a708921e3 BC: Fix #13 paste undo command. 2023-11-22 10:02:25 +01:00
Pierre-Antoine Rouby 9fe1b35a59 IC: Fix profile side effect on IC generator. 2023-11-21 16:06:37 +01:00
Pierre-Antoine Rouby edf744eee1 Geometry: Fix profiles list sorting. 2023-11-21 14:24:31 +01:00
Pierre-Antoine Rouby e400980103 Frictions: Fix plot drawing crash. 2023-11-21 11:07:36 +01:00
Pierre-Antoine Rouby b12e935a5d Geometry: Fix some translate. 2023-11-21 10:45:00 +01:00
Pierre-Antoine Rouby 1d4422e9c5 MainWindow: Disable window maximized. 2023-11-21 10:38:35 +01:00
Pierre-Antoine Rouby 1a10da9c89 MainWindow: Fix translate and re organize UI. 2023-11-21 10:36:13 +01:00
Pierre-Antoine Rouby 470eae8b92 pamhyr: Fix #20 study reload whene study is not saved on a file. 2023-11-21 09:26:57 +01:00
Pierre-Antoine Rouby 9b53b4302b BC,IC: Enable to get time int from float string (fix #21). 2023-11-21 09:11:35 +01:00
Pierre-Antoine Rouby 461202219d Window: Fix paste delimiter. 2023-11-20 17:29:37 +01:00
Pierre-Antoine Rouby ddfaa3a73f git: Add pip requirements. 2023-11-20 16:35:32 +01:00
Pierre-Antoine Rouby 3e531130fe Merge branch 'master' of gitlab-ssh.irstea.fr:theophile.terraz/pamhyr 2023-11-20 16:16:24 +01:00
Pierre-Antoine Rouby 595eb29c42 BC: Fix long type translate crash. 2023-11-20 16:15:43 +01:00
Pierre-Antoine Rouby ccbbc83502 IC: Fix sql_save as generator value type. 2023-11-20 16:10:10 +01:00
Pierre-Antoine Rouby fd0a8ddf51 BC: Fix paste command. 2023-11-20 15:55:57 +01:00
Pierre-Antoine Rouby ce5808e3ce Geometry: Fix delete profiles. 2023-11-20 15:38:52 +01:00
Pierre-Antoine Rouby 4dafa345b4 Geometry: Fix delete command crash. 2023-11-20 15:37:07 +01:00
Theophile Terraz 90776f0af9 Merge branch 'master' of gitlab-ssh.irstea.fr:theophile.terraz/pamhyr 2023-11-02 16:44:02 +01:00
Theophile Terraz 32777456b9 add scripts to use Mascaret 2023-11-02 16:43:57 +01:00
Pierre-Antoine Rouby dcaa7fafbd tools: Fix PEP8. 2023-10-30 09:30:16 +01:00
Pierre-Antoine Rouby 17eb3e8184 tools: parser: Minor change and tests fixes. 2023-10-27 17:21:09 +02:00
Pierre-Antoine Rouby 7bbca7cf54 unittests: Add parse tests for ' quote. 2023-10-27 15:10:16 +02:00
Pierre-Antoine Rouby 3c738ebcb9 Solver: Mage: Fix profile sediment export. 2023-10-27 14:32:51 +02:00
Pierre-Antoine Rouby 6522cea03f pamhyr: Set axis label color to 'black' instead 'green'. 2023-10-27 14:23:55 +02:00
Pierre-Antoine Rouby ea2dbfba3e pamhyr: Rename '_sql_*' function to '_db_*'. 2023-10-27 14:15:42 +02:00
Pierre-Antoine Rouby 8d92fbbdac pamhyr: Delete useless '__init__.py' file. 2023-10-27 14:13:01 +02:00
Pierre-Antoine Rouby 77f06aaa07 tools: Time the parser function. 2023-10-27 11:53:00 +02:00
Pierre-Antoine Rouby b79c5b30c0 tests: Add tests for flatten function. 2023-10-27 11:50:36 +02:00
Pierre-Antoine Rouby fdac834d48 Tests: Fix pep8. 2023-10-27 11:34:11 +02:00
Pierre-Antoine Rouby 4355be1a9c Solver: Mage: Split ST export function. 2023-10-27 11:31:20 +02:00
Pierre-Antoine Rouby 66f6d3505b Solver: CommandLine: Fix path replace. 2023-10-27 11:15:58 +02:00
Pierre-Antoine Rouby 65c261887c Tools: Command line parser: Add a test. 2023-10-27 11:14:10 +02:00
Pierre-Antoine Rouby 6c794d2eb4 Solver: Refactoring command line parser and add unittest. 2023-10-27 11:05:16 +02:00
Pierre-Antoine Rouby 51c912f3a8 Solver: Code refactoring. 2023-10-27 09:33:32 +02:00
104 changed files with 8839 additions and 701 deletions

No files matched your search

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

File diff suppressed because it is too large. Load diff
+434
View File
@@ -0,0 +1,434 @@
1 0 0 8 31090.0000
31090.0000 0.0000 23.7300
31090.0000 2.5000 20.2800 rg
31090.0000 2.5000 19.2900
31090.0000 4.4000 19.2900
31090.0000 10.7000 19.5900
31090.0000 10.7000 20.5900 rd
31090.0000 14.5000 21.7100
31090.0000 17.6000 23.7300
999.9990 999.9990 999.9990
2 0 0 8 31165.0000
31165.0000 0.0000 22.8400
31165.0000 2.5000 20.1300 rg
31165.0000 2.5000 19.2300
31165.0000 4.4000 19.2300
31165.0000 10.7000 19.5300
31165.0000 10.7000 20.5300 rd
31165.0000 14.5000 21.7500
31165.0000 17.6000 23.5000
999.9990 999.9990 999.9990
3 0 0 10 31425.0000
31425.0000 -1.0000 22.8000
31425.0000 0.0000 21.9000
31425.0000 1.7000 20.3600 rg
31425.0000 2.5000 19.6300
31425.0000 2.5000 19.0300
31425.0000 4.4000 19.0300
31425.0000 10.7000 19.3300
31425.0000 10.7000 20.3300 rd
31425.0000 14.5000 21.9200
31425.0000 17.6000 23.4200
999.9990 999.9990 999.9990
4 0 0 10 31590.0000
31590.0000 -1.0000 22.7500
31590.0000 0.0000 22.0500
31590.0000 1.6700 20.2200 rg
31590.0000 2.5000 19.3100
31590.0000 2.5000 18.9000
31590.0000 4.4000 18.9000
31590.0000 10.7000 19.2000
31590.0000 10.7000 20.2000 rd
31590.0000 14.5000 22.0200
31590.0000 17.6000 23.2500
999.9990 999.9990 999.9990
5 0 0 10 31780.0000
31780.0000 -1.0000 22.7000
31780.0000 0.0000 22.0500
31780.0000 1.6000 20.0500 rg
31780.0000 2.5000 18.9400
31780.0000 2.5000 18.7500
31780.0000 4.4000 18.7500
31780.0000 10.7000 19.0500
31780.0000 10.7000 20.0500 rd
31780.0000 14.5000 22.1400
31780.0000 17.6000 23.0000
999.9990 999.9990 999.9990
6 0 0 9 31874.0000
31874.0000 0.0000 22.9900
31874.0000 0.0000 22.6900
31874.0000 2.5000 19.6900 rg
31874.0000 2.5000 18.6900
31874.0000 4.4000 18.6900
31874.0000 10.7000 18.9900
31874.0000 10.7000 19.9900 rd
31874.0000 14.5000 22.1900
31874.0000 17.6000 22.9900
999.9990 999.9990 999.9990
7 0 0 7 31875.0000 PontRD101m
31875.0000 0.5400 21.8900
31875.0000 2.5000 19.6900 rg
31875.0000 2.5000 18.6900
31875.0000 4.4000 18.6900
31875.0000 10.7000 18.9900
31875.0000 10.7000 19.9900 rd
31875.0000 13.8900 21.8900
999.9990 999.9990 999.9990
8 0 0 7 31885.0000 PontRD101v
31885.0000 0.5400 21.8900
31885.0000 2.5000 19.6900 rg
31885.0000 2.5000 18.6900
31885.0000 4.4000 18.6900
31885.0000 10.7000 18.9900
31885.0000 10.7000 19.9900 rd
31885.0000 13.8900 21.8900
999.9990 999.9990 999.9990
9 0 0 9 31886.0000
31886.0000 0.0000 22.9900
31886.0000 0.0000 22.6900
31886.0000 2.5000 19.6900 rg
31886.0000 2.5000 18.6900
31886.0000 4.4000 18.6900
31886.0000 10.7000 18.9900
31886.0000 10.7000 19.9900 rd
31886.0000 14.5000 22.1900
31886.0000 17.6000 22.9900
999.9990 999.9990 999.9990
10 0 0 13 32015.0000
32015.0000 0.0000 22.6400
32015.0000 0.2000 22.2200
32015.0000 1.3000 20.9900
32015.0000 2.0000 19.8800 rg
32015.0000 3.0000 18.6600
32015.0000 3.0000 18.5800
32015.0000 5.5000 18.5800
32015.0000 10.0000 18.9100
32015.0000 11.5000 19.2500
32015.0000 11.5000 20.0800 rd
32015.0000 15.1000 22.0600
32015.0000 17.6000 22.5100
32015.0000 18.0000 22.8100
999.9990 999.9990 999.9990
11 0 0 13 32205.0000
32205.0000 0.0000 22.3300
32205.0000 0.2000 22.1800
32205.0000 1.5000 21.0600
32205.0000 2.5000 19.7700 rg
32205.0000 3.6000 18.5100
32205.0000 3.6000 18.4500
32205.0000 6.9000 18.4500
32205.0000 8.8000 19.0500
32205.0000 12.5000 19.5700 rd
32205.0000 12.5000 20.1800
32205.0000 15.8000 21.9000
32205.0000 18.1000 22.4000
32205.0000 18.6000 22.5500
999.9990 999.9990 999.9990
12 0 0 13 32460.0000
32460.0000 0.0000 22.1500
32460.0000 0.3000 22.1300
32460.0000 1.8000 21.1600
32460.0000 3.0000 19.6400 rg
32460.0000 4.4000 18.3100
32460.0000 4.4000 18.2800
32460.0000 8.7000 18.2800
32460.0000 10.4000 19.2400 rd
32460.0000 13.7000 19.9900
32460.0000 13.7000 20.3300
32460.0000 16.8000 21.6800
32460.0000 18.9000 22.2500
32460.0000 19.3000 22.5500
999.9990 999.9990 999.9990
13 0 0 13 32655.0000
32655.0000 0.0000 22.4500
32655.0000 0.3000 22.0900
32655.0000 2.1000 21.2300
32655.0000 3.4000 19.5300 rg
32655.0000 5.0000 18.1600
32655.0000 5.0000 18.1400
32655.0000 10.1000 18.1400
32655.0000 11.6000 19.3900 rd
32655.0000 14.7000 20.3100
32655.0000 14.7000 20.4300
32655.0000 17.5000 21.5200
32655.0000 19.4000 22.1400
32655.0000 19.8000 22.4500
999.9990 999.9990 999.9990
14 0 0 13 32755.0000
32755.0000 0.0000 22.4500
32755.0000 0.3000 22.0900
32755.0000 2.1000 21.2300
32755.0000 3.4000 19.5300 rg
32755.0000 4.0000 18.0500
32755.0000 5.0000 18.0100
32755.0000 10.1000 18.0100
32755.0000 13.0000 19.3900 rd
32755.0000 14.7000 20.3100
32755.0000 14.7000 20.4300
32755.0000 17.5000 21.5200
32755.0000 19.4000 22.1400
32755.0000 19.8000 22.4500
999.9990 999.9990 999.9990
15 0 0 10 32770.0000 PontA2
32770.0000 0.0000 25.0000
32770.0000 1.9000 25.0000
32770.0000 1.9000 21.2700
32770.0000 3.3000 19.4700 rg
32770.0000 5.0000 18.0100
32770.0000 10.5000 18.0100
32770.0000 12.0000 19.4700 rd
32770.0000 19.4000 21.4700
32770.0000 19.4000 25.0000
32770.0000 20.3000 25.0000
999.9990 999.9990 999.9990
16 0 0 10 32800.0000 PontA2
32800.0000 0.0000 25.0000
32800.0000 1.9000 25.0000
32800.0000 1.9000 21.2700
32800.0000 3.3000 19.4700 rg
32800.0000 5.0000 18.0100
32800.0000 10.5000 18.0100
32800.0000 12.0000 19.4700 rd
32800.0000 19.4000 21.4700
32800.0000 19.4000 25.0000
32800.0000 20.3000 25.0000
999.9990 999.9990 999.9990
17 0 0 13 32820.0000
32820.0000 0.0000 22.4500
32820.0000 0.3000 22.0900
32820.0000 2.1000 21.2300
32820.0000 3.4000 19.5300 rg
32820.0000 4.0000 18.0500
32820.0000 5.0000 18.0100
32820.0000 10.1000 18.0100
32820.0000 13.0000 19.3900 rd
32820.0000 14.7000 20.3100
32820.0000 14.7000 20.4300
32820.0000 17.5000 21.5200
32820.0000 19.4000 22.1400
32820.0000 19.8000 22.4500
999.9990 999.9990 999.9990
18 0 0 11 32915.0000
32915.0000 0.0000 22.5500
32915.0000 0.4000 21.9500
32915.0000 1.7000 21.2200
32915.0000 2.4000 20.9700
32915.0000 3.8000 19.4100 rg
32915.0000 3.9000 19.3100
32915.0000 5.8000 18.0100
32915.0000 11.0000 18.0100
32915.0000 12.4000 19.3400 rd
32915.0000 19.4000 21.9900
32915.0000 19.7000 22.5500
999.9990 999.9990 999.9990
19 0 0 10 33140.0000
33140.0000 0.4000 21.7700
33140.0000 1.9000 20.7600
33140.0000 2.7000 20.5100
33140.0000 4.2000 19.1500 rg
33140.0000 4.3000 19.0600
33140.0000 6.4000 17.9200
33140.0000 11.3000 17.9200
33140.0000 12.6000 19.1400 rd
33140.0000 18.9000 21.8700
33140.0000 19.2000 22.1800
999.9990 999.9990 999.9990
20 0 0 10 33425.0000
33425.0000 0.0000 21.6800
33425.0000 0.5000 21.5300
33425.0000 2.2000 20.1600
33425.0000 3.0000 19.9400
33425.0000 4.8000 18.8200 rg
33425.0000 4.9000 18.7500
33425.0000 7.2000 17.8000
33425.0000 11.7000 17.8000
33425.0000 12.8000 18.8800 rd
33425.0000 18.2000 21.6800
999.9990 999.9990 999.9990
21 0 0 10 33685.0000
33685.0000 0.0000 21.5300
33685.0000 0.5000 21.3200
33685.0000 2.4000 19.6200
33685.0000 3.3000 19.4200
33685.0000 5.2000 18.5300 rg
33685.0000 5.4000 18.4700
33685.0000 7.9000 17.7000
33685.0000 12.0000 17.7000
33685.0000 12.9000 18.6500 rd
33685.0000 17.6000 21.5300
999.9990 999.9990 999.9990
22 0 0 9 33920.0000
33920.0000 0.6000 20.9800
33920.0000 2.6000 19.1300
33920.0000 3.6000 18.9400
33920.0000 5.7000 18.2600 rg
33920.0000 5.8000 18.2200
33920.0000 8.5000 17.6000
33920.0000 12.3000 17.6000
33920.0000 13.1000 18.4400 rd
33920.0000 17.0000 21.1300
999.9990 999.9990 999.9990
23 0 0 9 34095.0000
34095.0000 0.6000 20.9500
34095.0000 2.7000 18.7700
34095.0000 3.8000 18.5900
34095.0000 6.0000 18.0600 rg
34095.0000 6.1000 18.0300
34095.0000 9.0000 17.5300
34095.0000 12.5000 17.5300
34095.0000 13.2000 18.2800 rd
34095.0000 16.6000 20.9800
999.9990 999.9990 999.9990
24 0 0 10 34295.0000
34295.0000 0.0000 21.3600
34295.0000 0.6000 20.8300
34295.0000 2.4000 18.3500
34295.0000 3.5000 18.1900
34295.0000 5.8000 17.8300 rg
34295.0000 6.5000 17.3400
34295.0000 9.5000 17.3600
34295.0000 12.8000 17.4100
34295.0000 14.0000 18.1000 rd
34295.0000 16.1000 21.3600
999.9990 999.9990 999.9990
25 0 0 10 34334.0000 amontPontThivencel
34334.0000 0.0000 21.3600
34334.0000 0.6000 20.8300
34334.0000 2.4000 18.3500
34334.0000 3.5000 18.1900
34334.0000 5.8000 17.8300 rg
34334.0000 6.5000 17.3400
34334.0000 9.5000 17.3600
34334.0000 12.8000 17.4100
34334.0000 14.0000 18.1000 rd
34334.0000 16.1000 21.1300
999.9990 999.9990 999.9990
26 0 0 7 34335.0000 PontmThivencelle
34335.0000 2.9000 20.7400
34335.0000 2.9000 18.2900 rg
34335.0000 6.4000 17.7900
34335.0000 9.6000 17.2600
34335.0000 12.8000 17.2600
34335.0000 12.8000 18.3100 rd
34335.0000 12.8000 20.7400
999.9990 999.9990 999.9990
27 0 0 7 34343.0000 PontvThivencelle
34343.0000 2.9000 20.7400
34343.0000 2.9000 18.2900 rg
34343.0000 6.4000 17.7900
34343.0000 9.6000 17.2600
34343.0000 12.8000 17.2600
34343.0000 12.8000 18.2900 rd
34343.0000 12.8000 20.7400
999.9990 999.9990 999.9990
28 0 0 10 34345.0000 avalPontThivencell
34345.0000 0.0000 21.3600
34345.0000 0.6000 20.8300
34345.0000 2.4000 18.3500
34345.0000 3.5000 18.1900
34345.0000 5.8000 17.8300 rg
34345.0000 6.5000 17.3400
34345.0000 9.5000 17.2600
34345.0000 12.8000 17.4100
34345.0000 14.0000 18.1000 rd
34345.0000 16.1000 21.1300
999.9990 999.9990 999.9990
29 0 0 7 34405.0000
34405.0000 0.0000 21.1400
34405.0000 2.9000 17.8400 rg
34405.0000 6.4000 17.4400
34405.0000 9.6000 17.2000
34405.0000 12.8000 17.2200
34405.0000 13.9000 17.8400 rd
34405.0000 19.0000 21.0000
999.9990 999.9990 999.9990
30 0 0 7 34535.0000
34535.0000 0.0000 20.5900
34535.0000 2.9000 17.7400 rg
34535.0000 6.4000 17.2400
34535.0000 9.6000 17.0300
34535.0000 12.8000 17.0300
34535.0000 13.5000 17.7400 rd
34535.0000 15.8000 20.6000
999.9990 999.9990 999.9990
31 0 0 7 34705.0000
34705.0000 0.0000 20.5000
34705.0000 2.9000 17.6100 rg
34705.0000 6.4000 17.1100
34705.0000 9.6000 16.8500
34705.0000 12.8000 16.8500
34705.0000 14.0000 18.0000 rd
34705.0000 16.2000 20.5300
999.9990 999.9990 999.9990
32 0 0 7 34825.0000
34825.0000 0.0000 20.6200
34825.0000 2.9000 17.5200 rg
34825.0000 6.4000 17.0200
34825.0000 9.6000 16.7100
34825.0000 12.8000 16.7100
34825.0000 13.5000 17.5200 rd
34825.0000 16.2000 20.5000
999.9990 999.9990 999.9990
33 0 0 7 34995.0000
34995.0000 0.0000 20.3900
34995.0000 2.9000 17.3900 rg
34995.0000 6.4000 16.8900
34995.0000 9.6000 16.5300
34995.0000 12.8000 16.5300
34995.0000 13.6000 17.4300 rd
34995.0000 16.2000 20.3200
999.9990 999.9990 999.9990
34 0 0 7 35135.0000
35135.0000 0.0000 20.1600
35135.0000 2.9000 17.2900 rg
35135.0000 6.4000 16.7900
35135.0000 9.6000 16.4300
35135.0000 12.8000 16.4300
35135.0000 13.9000 17.3700 rd
35135.0000 16.2000 20.0600
999.9990 999.9990 999.9990
35 0 0 7 35360.0000
35360.0000 0.0000 19.8600
35360.0000 2.9000 17.1200 rg
35360.0000 6.4000 16.6200
35360.0000 9.6000 16.2600
35360.0000 12.8000 16.2600
35360.0000 13.7000 17.1200 rd
35360.0000 16.2000 19.4600
999.9990 999.9990 999.9990
36 0 0 7 35580.0000
35580.0000 0.0000 20.0000
35580.0000 2.9000 16.9500 rg
35580.0000 3.0000 16.0900
35580.0000 11.5000 16.0500
35580.0000 20.0000 16.0900
35580.0000 21.0000 17.0300 rd
35580.0000 23.0000 19.5000
999.9990 999.9990 999.9990
37 0 0 7 35920.0000
35920.0000 0.0000 20.0000
35920.0000 2.2000 16.0200 rg
35920.0000 3.0000 14.5500
35920.0000 11.5000 14.5000
35920.0000 20.0000 14.5500
35920.0000 20.9000 16.0200 rd
35920.0000 23.0000 19.5000
999.9990 999.9990 999.9990
38 0 0 7 36270.0000 S1
36270.0000 0.0000 20.0000
36270.0000 1.8000 17.0100 rg
36270.0000 3.0000 15.0800
36270.0000 11.5000 15.0500
36270.0000 20.0000 15.0800
36270.0000 21.3000 17.0100 rd
36270.0000 23.0000 19.5000
999.9990 999.9990 999.9990
39 0 0 7 36271.0000 S1bis
36271.0000 0.0000 20.0000
36271.0000 1.8000 17.0100 rg
36271.0000 3.0000 13.0800
36271.0000 11.5000 13.0500
36271.0000 20.0000 13.0800
36271.0000 21.3000 17.0100 rd
36271.0000 23.0000 19.5000
999.9990 999.9990 999.9990
+2
View File
@@ -0,0 +1,2 @@
0:00:00:00 15
0:00:01:00 15
+25
View File
@@ -0,0 +1,25 @@
0:00:00:00 5,53
0:11:37:00 5,72
0:16:17:00 5,82
0:22:57:00 5,92
1:00:00:00 6,6
1:00:57:00 8,04
1:02:29:00 14,3
1:04:38:00 24,4
1:08:48:00 40
1:11:27:00 46,3
1:13:47:00 48,8
1:16:07:00 47,9
1:17:17:00 44,8
1:20:23:00 27,8
1:21:57:00 24,3
2:01:15:00 19,2
2:05:15:00 15,8
2:11:25:00 11,5
2:11:57:00 11,1
2:13:17:00 10,5
2:19:07:00 9,84
2:23:59:00 8,43
3:04:51:00 7,02
3:09:43:00 5,61
3:14:35:00 5,53
+25
View File
@@ -0,0 +1,25 @@
**************** SINGULARITES **********************
*BieSec Nb Longueur Cote dev Cote charg Coef deb Impression
*===---===----------==========----------==========-----------------------------
*****************************************************
* Bief n° 1
*****************************************************
* Ouvrage au pk 36270.0 seuil_aval
*SeuilR type D
D 1 36270.00 18.250 15.500 0.000 0.400 99999.000 seuil palplanches
* Ouvrage au pk 31875.0 PontRS101
*OrificeR type O
O 1 31875.00 9.660 18.710 21.890 0.400 9999.000
*SeuilR type D
D 1 31875.00 14.000 22.890 0.000 0.400 99999.000
* Ouvrage au pk 34335.0 PontThivencelle
*SeuilR type D
D 1 34335.00 13.000 21.740 0.000 0.400 99999.000
*OrificeR type O
O 1 34335.00 9.070 17.280 20.740 0.400 9999.000
*****************************************************
* Bief n° 3
*****************************************************
* Ouvrage au pk 0.0 Orifice
*OrificeR type O
*O 3 0.000 6.000 19.000 19.001 0.400 20.000 Clapet_001
+1
View File
@@ -0,0 +1 @@
$Clapet_001 clapet
+2
View File
@@ -0,0 +1,2 @@
0:00:00:00 4
0:00:01:00 4
+24
View File
@@ -0,0 +1,24 @@
16,03 0
16,13 1,29
16,23 3,64
16,33 6,7
16,43 10,31
16,53 14,41
16,63 18,94
16,73 23,87
16,83 29,16
16,93 34,79
17,03 40,75
17,13 47,01
17,23 53,57
17,33 60,4
17,43 67,5
17,53 74,86
17,63 82,47
17,73 90,33
17,83 98,41
17,93 106,73
18,03 115,26
18,13 124,01
18,23 132,98
18,33 142,14
+9
View File
@@ -0,0 +1,9 @@
0:00:00:00 4
0:01:16:48 4
0:16:00:00 50
1:11:46:28 36,8
2:07:27:22 23,3
5:04:24:58 13,8
10:03:00:06 9,38
20:02:19:58 7,29
30:01:54:37 5,97
+10
View File
@@ -0,0 +1,10 @@
0:00:00:00 4
0:01:49:48 4
0:16:00:00 35
1:11:25:48 25
2:07:30:00 16,4
5:03:55:12 9,66
10:03:00:00 6,56
27:00:54:00 4,91
30:01:48:36 4
31:06:00:00 4
Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

+201
View File
@@ -0,0 +1,201 @@
%% LyX 2.0.2 created this file. For more info, see http://www.lyx.org/.
%% Do not edit unless you really know what you are doing.
\documentclass[12pt,french]{article}
\usepackage[T1]{fontenc}
\usepackage[latin9]{inputenc}
\usepackage{geometry}
\geometry{verbose,tmargin=2cm,bmargin=2cm,lmargin=2cm,rmargin=2cm,headheight=2cm,headsep=2cm,footskip=2cm}
\usepackage{textcomp}
\usepackage{graphicx}
\usepackage{hyperref}
\makeatletter
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% LyX specific LaTeX commands.
\newcommand{\noun}[1]{\textsc{#1}}
%% Because html converters don't know tabularnewline
\providecommand{\tabularnewline}{\\}
\makeatother
\usepackage{babel}
\addto\extrasfrench{%
\providecommand{\og}{\leavevmode\flqq~}%
\providecommand{\fg}{\ifdim\lastskip>\z@\unskip\fi~\frqq}%
}
\begin{document}
\includegraphics[width=5cm]{Logo-INRAE_Transparent.png}
~
~
~
\begin{center}
Tutorial for Pamhyr2
January 2024
\par\end{center}
~
~
\begin{center}
\textbf{\LARGE 1D Modeling of the Hogneau River (Nord, France)}
using Pamhyr2
\par\end{center}{\LARGE \par}
~
~
\begin{center}
{\large INRAE Lyon-Grenoble Auvergne-Rhône-Alpes}
\par\end{center}{\large \par}
\begin{center}
Riverly, river hydraulics
\par\end{center}
~
~
~
~
~
~
\begin{center}
\begin{tabular}{lll}
Autors : & Pierre-Antoine Rouby & pierre-antoine.rouby@inrae.fr\tabularnewline
& Théophile TERRAZ & theophile.terraz@inrae.fr\tabularnewline
\end{tabular}
\par\end{center}
~
\pagebreak{}
\begin{center}
\tableofcontents{}
\par\end{center}
~
\pagebreak{}
\section{Introduction}
TODO
\pagebreak{}
\section{Install Pamhyr2}
Pamhyr2 can be downloaded from \url{https://gitlab.irstea.fr/theophile.terraz/pamhyr}.
\begin{center}
\includegraphics[width=15cm]{dl.png}
\par\end{center}
Use the GNU Linux or the Windows download button depending on your system. On windows, launch the installer. On Linux, unpack the archive and launch Pamhyr2.
\section{Create your first study}
On the main windows, click on \texttt{[Files] => [New Study]} to create a new study.
Click on \texttt{[River Network] => [Edit River Network]} to create the reaches of your river. In this window, you must define an oriented graph that represents the reaches of your river network: the edges are the reaches and the nodes are either upstream boundary conditions, downstream boundary conditions or junctions.
Press the \includegraphics[width=0.5cm]{gtk_add.png} to enter the \textit{add} mode. Create two nodes by clicking in the grey zone of the window, and create a link by clicking again on each node. press \includegraphics[width=0.5cm]{gtk_add.png} again to exit the \textit{add} mode. You created your first reach, with an upstream node and a downstream node. In the lower part of the \textit{Edit River Network} window you can rename the nodes and the reaches. As the reach you created is automaticaly selected, all the next steps will apply to this reach. The window should look like that:
\begin{center}
\includegraphics[width=15cm]{network.png}
\par\end{center}
Close the \textit{Edit River Network} window.
\section{Edit the river geometry}
Click on \texttt{[Geometry] => [Edit Geometry]} to define the geometry of the selected bief. Click on the \texttt{[Import]} button and select the file \texttt{Data/Bief\_1.st}. You should see:
\begin{center}
\includegraphics[width=15cm]{Geo.png}
\par\end{center}
On the left panel is a list of all the cross sections with their name and longitudinal abscisa. In the top left plot you can see the top view of the river, on the top right panel the longitudinal cross-section of the river and in the bottom plot you can see the selected cross-section (blue) along with the next one (dashed purple) and previous one (dashed black). You can move in the section list using the slider at the very right of the window.
You can edit the selected cross section by clicking on the \includegraphics[width=0.5cm]{"../../../src/View/ui/ressources/edit.png"} icon.
select the cross section named \textit{PontRD101m} and open the edition window. You should see:
\begin{center}
\includegraphics[width=15cm]{editsect.png}
\par\end{center}
On the left panel is the list of all the points of the section, with their coordinates, their name and their transversal absisa. The Z coordinate of the highest point is written in blue and the lowest in red. Points can have a name. If a point with the same name exists in every section in a reach, it forms a longitudinal line. For example, here we have \textit{rg} and \textit{rd} which represent the left bank and the right bank of the main chanel.
On the plot is a projection of the cross section. You can use \texttt{[ctrl + click]} to select a point in the plot. For now we don't have to change anything. You can close the cross section edition window and the geometry edition window.
\section{Edit the boundary conditions}
From the main window, click on \texttt{[Hydraulics] => [Boundary conditions and punctual contributions]}.You should see:
\begin{center}
\includegraphics[width=15cm]{boundary.png}
\par\end{center}
Use the \textit{add} button on the top left of the window to add a liquid boundary condition.
On the new line, click to select the whole line, double click to select the cell.
Select the \textit{Type} cell to give a name to the boundary condition. here, we will define the flow discharge mesured during the february 2002 flood. You can name this boundary condition "flood2002".
Select the \textit{Type} cell and use the combo box to put a Q(t) law.
Select the \textit{Node} cell and atribute this condition to the upstream node.
Names of the nodes are recalled in network in the right panel.
Now select the whole line and click on the edit button \includegraphics[width=0.5cm]{"../../../src/View/ui/ressources/edit.png"}. You opened the \textit{Edit Boundary Conditions} window.
On a text editor, open the \texttt{Data/Fevrier\_2002.txt} file. Copy the content of the file (for example with \textit{ctrl+a ctrl+c}) and paste it in the left panel of the \textit{Edit Boundary Conditions} window with \textit{ctrl+v}. You can now see the flow discharge curve:
\begin{center}
\includegraphics[width=15cm]{fev2002.png}
\par\end{center}
Close this window. Go back on the \textit{Boundary Conditions} window. Add a new line, give it a name, give it the textit{Z(T)} type and associate it to the downstream node of the network. Open the \textit{Edit Boundary Conditions} window (\includegraphics[width=0.5cm]{"../../../src/View/ui/ressources/edit.png"}). Add two lines. In the first one, enter time: 0.00.00 and Z: 15.000.
On the second one, time: 1.00.00 and Z: 15.000. It creates a constant downstream water elevation. For the computaion, Mage will extrapolate continuously the water elevation, that's why we only need to define one hour.
You can close the the \textit{Edit Boundary Conditions} and the \textit{Boundary Conditions} window.
\section{Create initial conditions}
From the main window, click on \texttt{[Hydraulics] => [Initial conditions]}.
Click on \texttt{Generate minimal height} and enter a height of 2 meters in the pop-up window to generate an initial water height condition that verify the folowing conditions: a water height of at least 2 meters and a decreasing water surface elevation from upstream to downstream. You should see:
\begin{center}
\includegraphics[width=15cm]{ic.png}
\par\end{center}
Close the \textit{Initial conditions} window.
\section{Edit friction coefficients}
From the main window, click on \texttt{[Hydraulics] => [Edit friction]}.
You fist have to define sets of Strickler coefficients. Click on \includegraphics[width=0.5cm]{"../../../src/View/ui/ressources/edit.png"} to open the \textit{Strickler} window. Here you can create couples of Strickler coefficients, the first one for the minor bed, the second one for the medium bed. Click on \textit{add} four times to create four new couple. Give them the folowing values:
\begin{center}
\includegraphics[width=15cm]{K.png}
\par\end{center}
Close the \textit{Strickler} window. On the \textit{Edit friction} window, add four lines with the button \includegraphics[width=0.5cm]{gtk_add.png} to create fous friction zones. Each zone is defined by a \textit{begin} and \textit{end} PK and a \textit{begin} and \textit{end} Strickler couple. The strickler coefficient couples inside a zone are interpolated from the \textit{begin} and \textit{end} couples. In our case, we will use constant coefficients per zone. Set the zones as follow:
\begin{center}
\includegraphics[width=15cm]{frictions.png}
\par\end{center}
\pagebreak{}
\end{document}
+1
View File
@@ -9,3 +9,4 @@ numpy>=1.24.2
colorama>=0.4.3
pyinstaller>=5.11.0
shapely>=2.0.1
lxml>=4.9.3
+1
View File
@@ -9,3 +9,4 @@ numpy>=1.24.2
colorama>=0.4.3
pyinstaller>=5.11.0
shapely>=2.0.1
lxml>=4.9.3
@@ -51,7 +51,7 @@ class BoundaryCondition(SQLSubModel):
BoundaryCondition._id_cnt = max(BoundaryCondition._id_cnt + 1, self.id)
@classmethod
def _sql_create(cls, execute):
def _db_create(cls, execute):
execute("""
CREATE TABLE boundary_condition(
id INTEGER NOT NULL PRIMARY KEY,
@@ -77,7 +77,7 @@ class BoundaryCondition(SQLSubModel):
return cls._create_submodel(execute)
@classmethod
def _sql_update(cls, execute, version):
def _db_update(cls, execute, version):
return True
@classmethod
@@ -102,7 +102,7 @@ class BoundaryCondition(SQLSubModel):
return res
@classmethod
def _sql_load(cls, execute, data=None):
def _db_load(cls, execute, data=None):
new = []
tab = data["tab"]
@@ -144,7 +144,7 @@ class BoundaryCondition(SQLSubModel):
return new
def _sql_save(self, execute, data=None):
def _db_save(self, execute, data=None):
tab = data["tab"]
execute(f"DELETE FROM boundary_condition WHERE id = {self.id}")
@@ -158,16 +158,16 @@ class BoundaryCondition(SQLSubModel):
"INSERT INTO " +
"boundary_condition(id, name, type, tab, node) " +
"VALUES (" +
f"{self.id}, '{self._sql_format(self._name)}', " +
f"'{self._sql_format(self._type)}', '{tab}', {node}" +
f"{self.id}, '{self._db_format(self._name)}', " +
f"'{self._db_format(self._type)}', '{tab}', {node}" +
")"
)
execute(sql)
ind = 0
for d in self._data:
data0 = self._sql_format(str(d[0]))
data1 = self._sql_format(str(d[1]))
data0 = self._db_format(str(d[0]))
data1 = self._db_format(str(d[1]))
sql = (
"INSERT INTO " +
@@ -188,8 +188,13 @@ class BoundaryCondition(SQLSubModel):
@classmethod
def time_convert(cls, data):
if data is str and data.count(":") == 3:
return old_pamhyr_date_to_timestamp(data)
if type(data) is str:
if data.count(":") == 3:
return old_pamhyr_date_to_timestamp(data)
if data.count(":") == 2:
return old_pamhyr_date_to_timestamp("00:" + data)
if data.count(".") == 1:
return round(float(data))
return int(data)
@@ -38,7 +38,7 @@ class BoundaryConditionList(PamhyrModelListWithTab):
]
@classmethod
def _sql_load(cls, execute, data=None):
def _db_load(cls, execute, data=None):
new = cls(status=data['status'])
if data is None:
@@ -46,13 +46,13 @@ class BoundaryConditionList(PamhyrModelListWithTab):
for tab in new._tabs:
data["tab"] = tab
new._tabs[tab] = BoundaryCondition._sql_load(
new._tabs[tab] = BoundaryCondition._db_load(
execute, data
)
return new
def _sql_save(self, execute, data=None):
def _db_save(self, execute, data=None):
execute("DELETE FROM boundary_condition")
if data is None:
@@ -61,7 +61,7 @@ class BoundaryConditionList(PamhyrModelListWithTab):
for tab in self._tabs:
data["tab"] = tab
for bc in self._tabs[tab]:
bc._sql_save(execute, data=data)
bc._db_save(execute, data=data)
return True
@@ -89,3 +89,16 @@ class BoundaryConditionList(PamhyrModelListWithTab):
def copy(self):
return copy(self)
def get_assoc_to_node(self, tab, node):
assoc = list(
filter(
lambda i: i.node is node,
self._tabs[tab]
)
)
if len(assoc) > 0:
return assoc[0]
return None
+13 -4
View File
@@ -35,7 +35,7 @@ class Friction(SQLSubModel):
self._end_strickler = None
@classmethod
def _sql_create(cls, execute):
def _db_create(cls, execute):
execute("""
CREATE TABLE friction(
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
@@ -54,11 +54,11 @@ class Friction(SQLSubModel):
return True
@classmethod
def _sql_update(cls, execute, version):
def _db_update(cls, execute, version):
return True
@classmethod
def _sql_load(cls, execute, data=None):
def _db_load(cls, execute, data=None):
new = []
reach = data["parent"] # Reach object
status = data["status"]
@@ -88,7 +88,7 @@ class Friction(SQLSubModel):
yield ind, sec
def _sql_save(self, execute, data=None):
def _db_save(self, execute, data=None):
ind = data["ind"]
execute(
"INSERT INTO " +
@@ -129,6 +129,15 @@ class Friction(SQLSubModel):
def has_edge(self):
return self._edge is not None
def has_coefficient(self):
return (
self._begin_strickler is not None and
self._end_strickler is not None
)
def is_full_defined(self):
return self.has_edge() and self.has_coefficient()
@property
def begin_kp(self):
return self._begin_kp
+10 -8
View File
@@ -35,36 +35,38 @@ class FrictionList(PamhyrModelList):
]
@classmethod
def _sql_update_0_0_1(cls, execute, version):
def _db_update_0_0_1(cls, execute, version):
execute("ALTER TABLE `section` RENAME TO `friction`")
@classmethod
def _sql_update(cls, execute, version):
def _db_update(cls, execute, version):
if version == "0.0.0":
logger.info(f"Update friction TABLE from {version}")
cls._sql_update_0_0_1(execute, version)
cls._db_update_0_0_1(execute, version)
return True
@classmethod
def _sql_load(cls, execute, data=None):
def _db_load(cls, execute, data=None):
new = cls(status=data['status'])
new._lst = Friction._sql_load(
new._lst = Friction._db_load(
execute, data
)
return new
def _sql_save(self, execute, data=None):
def _db_save(self, execute, data=None):
frictions = self.lst
reach = data["reach"]
execute(f"DELETE FROM friction WHERE reach = {reach.id}")
ok = True
ind = 0
for friction in self._lst:
for friction in frictions:
data["ind"] = ind
ok &= friction._sql_save(execute, data=data)
ok &= friction._db_save(execute, data=data)
ind += 1
return ok
+5 -5
View File
@@ -36,7 +36,7 @@ class PointXYZ(Point, SQLSubModel):
self._z = float(z)
@classmethod
def _sql_create(cls, execute):
def _db_create(cls, execute):
execute("""
CREATE TABLE geometry_pointXYZ(
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
@@ -55,7 +55,7 @@ class PointXYZ(Point, SQLSubModel):
return cls._create_submodel(execute)
@classmethod
def _sql_update(cls, execute, version):
def _db_update(cls, execute, version):
cls._update_submodel(execute, version)
major, minor, release = version.strip().split(".")
@@ -71,7 +71,7 @@ class PointXYZ(Point, SQLSubModel):
return True
@classmethod
def _sql_load(cls, execute, data=None):
def _db_load(cls, execute, data=None):
status = data["status"]
profile = data["profile"]
@@ -109,7 +109,7 @@ class PointXYZ(Point, SQLSubModel):
yield ind, new
def _sql_save(self, execute, data=None):
def _db_save(self, execute, data=None):
profile = data["profile"]
ind = data["ind"]
@@ -119,7 +119,7 @@ class PointXYZ(Point, SQLSubModel):
"INSERT OR REPLACE INTO " +
"geometry_pointXYZ(ind, name, x, y, z, profile, sl) " +
"VALUES (" +
f"{ind}, '{self._sql_format(self._name)}', " +
f"{ind}, '{self._db_format(self._name)}', " +
f"{self.x}, {self.y}, {self.z}, " +
f"{profile.id}, {sl}" +
")"
+6
View File
@@ -345,3 +345,9 @@ class Profile(object):
# Abstract method, must be implemented for in non abstract class
def get_station(self):
raise NotImplementedMethodeError(self, self.get_station)
# Computation method
# Abstract method for width approximation
def width_approximation(self):
raise NotImplementedMethodeError(self, self.width_approximation)
+37 -8
View File
@@ -69,7 +69,7 @@ class ProfileXYZ(Profile, SQLSubModel):
)
@classmethod
def _sql_create(cls, execute):
def _db_create(cls, execute):
execute("""
CREATE TABLE geometry_profileXYZ(
id INTEGER NOT NULL PRIMARY KEY,
@@ -89,7 +89,7 @@ class ProfileXYZ(Profile, SQLSubModel):
return cls._create_submodel(execute)
@classmethod
def _sql_update(cls, execute, version):
def _db_update(cls, execute, version):
major, minor, release = version.strip().split(".")
if major == minor == "0":
if int(release) < 2:
@@ -104,7 +104,7 @@ class ProfileXYZ(Profile, SQLSubModel):
return cls._update_submodel(execute, version)
@classmethod
def _sql_load(cls, execute, data=None):
def _db_load(cls, execute, data=None):
profiles = []
status = data["status"]
reach = data["reach"]
@@ -147,7 +147,7 @@ class ProfileXYZ(Profile, SQLSubModel):
)
data["profile"] = new
new._points = PointXYZ._sql_load(execute, data.copy())
new._points = PointXYZ._db_load(execute, data.copy())
yield ind, new
@@ -155,7 +155,7 @@ class ProfileXYZ(Profile, SQLSubModel):
# return profiles
def _sql_save(self, execute, data=None):
def _db_save(self, execute, data=None):
ok = True
ind = data["ind"]
@@ -166,20 +166,22 @@ class ProfileXYZ(Profile, SQLSubModel):
"geometry_profileXYZ(id, ind, name, reach, " +
"kp, num, code1, code2, sl) " +
"VALUES (" +
f"{self.id}, {ind}, '{self._sql_format(self._name)}', " +
f"{self.id}, {ind}, '{self._db_format(self._name)}', " +
f"{self.reach.id}, {self.kp}, {self.num}, " +
f"{self.code1}, {self.code1}, {sl}" +
")"
)
execute(sql)
points = self.points
data["profile"] = self
execute(f"DELETE FROM geometry_pointXYZ WHERE profile = {self.id}")
ind = 0
for point in self.points:
for point in points:
data["ind"] = ind
ok &= point._sql_save(execute, data)
ok &= point._db_save(execute, data)
ind += 1
return ok
@@ -285,6 +287,27 @@ class ProfileXYZ(Profile, SQLSubModel):
except IndexError:
raise IndexError(f"Invalid point index: {index}")
def get_point_by_name(self, name: str) -> PointXYZ:
"""Get point by name.
Args:
name: Point name.
Returns:
The point.
"""
try:
n_name = name.lower().strip()
return next(
filter(
lambda p: p.name.lower().strip() == n_name,
self.points
)
)
except Exception as e:
logger.debug(f"{e}")
raise IndexError(f"Invalid point name: {name}")
def add(self):
"""Add a new PointXYZ to profile.
@@ -413,3 +436,9 @@ class ProfileXYZ(Profile, SQLSubModel):
constant = station[index_profile_z_min[0]]
return list(map(lambda s: s - constant, station))
def width_approximation(self):
rg = self.get_point_by_name("rg")
rd = self.get_point_by_name("rd")
return abs(rg.dist(rd))
+115 -10
View File
@@ -52,36 +52,37 @@ class Reach(SQLSubModel):
self._guidelines = {}
@classmethod
def _sql_create(cls, execute):
def _db_create(cls, execute):
return cls._create_submodel(execute)
@classmethod
def _sql_update(cls, execute, version):
def _db_update(cls, execute, version):
return cls._update_submodel(execute, version)
@classmethod
def _sql_load(cls, execute, data=None):
def _db_load(cls, execute, data=None):
new = cls(status=data["status"], parent=data["parent"])
new._profiles = ProfileXYZ._sql_load(
new._profiles = ProfileXYZ._db_load(
execute,
data=data
)
return new
def _sql_save(self, execute, data=None):
def _db_save(self, execute, data=None):
profiles = self.profiles
# Delete old data
execute(f"DELETE FROM geometry_profileXYZ WHERE reach = {self.id}")
# execute(f"DELETE FROM geometry_pointXYZ")
if data is None:
data = {}
ind = 0
for profile in self.profiles:
for profile in profiles:
data["ind"] = ind
profile._sql_save(execute, data)
profile._db_save(execute, data)
ind += 1
return True
@@ -134,6 +135,10 @@ class Reach(SQLSubModel):
return self._profiles
@profiles.setter
def profiles(self, profiles):
self._profiles = profiles
def get_profiles_from_kp(self, kp):
return list(
filter(
@@ -215,7 +220,7 @@ class Reach(SQLSubModel):
)
)
self.profiles = list(
self._profiles = list(
filter(
lambda p: p not in profiles,
self.profiles
@@ -233,7 +238,7 @@ class Reach(SQLSubModel):
Returns:
Nothing.
"""
self.profiles = list(
self._profiles = list(
filter(
lambda p: p not in profiles,
self.profiles
@@ -573,3 +578,103 @@ class Reach(SQLSubModel):
file_st.write(" 999.9990 999.9990 999.9990")
file_st.write("\n")
def get_incline(self):
first = self.profile(0)
last = self.profile(len(self) - 1)
z_first = first.z_min()
kp_first = first.kp
z_last = last.z_min()
kp_last = last.kp
return (
(z_last - z_first)
/
(kp_last - kp_first)
)
def get_incline_mean(self):
profiles = self.profiles
previous = profiles[0]
incline_acc = 0
for profile in profiles[1:]:
z_first = previous.z_min()
kp_first = previous.kp
z_last = profile.z_min()
kp_last = profile.kp
incline_acc += (
(z_last - z_first)
/
(kp_last - kp_first)
)
previous = profile
return (incline_acc / (len(profiles) - 1))
def get_incline_median(self):
profiles = self.profiles
previous = profiles[0]
incline_acc = []
for profile in profiles[1:]:
z_first = previous.z_min()
kp_first = previous.kp
z_last = profile.z_min()
kp_last = profile.kp
incline_acc += [
(z_last - z_first)
/
(kp_last - kp_first)
]
previous = profile
incline_acc.sort()
return incline_acc[round(len(profiles)/2)]
def get_incline_median_mean(self):
profiles = self.profiles
previous = profiles[0]
incline_acc = []
for profile in profiles[1:]:
z_first = previous.z_min()
kp_first = previous.kp
z_last = profile.z_min()
kp_last = profile.kp
incline_acc += [
(z_last - z_first)
/
(kp_last - kp_first)
]
previous = profile
incline_acc.sort()
marge = round(len(incline_acc) * 0.1)
incline_set = incline_acc[marge:-marge]
logger.debug(f"+{incline_acc}")
logger.debug(f"-{incline_set}")
return (
reduce(
lambda acc, x: acc + x,
incline_set,
0.0
) / (len(incline_set))
)
@@ -16,12 +16,16 @@
# -*- coding: utf-8 -*-
import logging
from copy import copy, deepcopy
from tools import trace, timer
from functools import reduce
from Model.Tools.PamhyrDB import SQLSubModel
logger = logging.getLogger()
class Data(SQLSubModel):
def __init__(self, name: str = "",
@@ -52,7 +56,7 @@ class Data(SQLSubModel):
self._update_from_discharge()
@classmethod
def _sql_create(cls, execute):
def _db_create(cls, execute):
execute("""
CREATE TABLE initial_conditions(
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
@@ -70,11 +74,11 @@ class Data(SQLSubModel):
return cls._create_submodel(execute)
@classmethod
def _sql_update(cls, execute, version):
def _db_update(cls, execute, version):
return cls._update_submodel(execute, version)
@classmethod
def _sql_load(cls, execute, data=None):
def _db_load(cls, execute, data=None):
id = data["reach"].id
table = execute(
"SELECT ind, name, comment, kp, discharge, height " +
@@ -109,7 +113,7 @@ class Data(SQLSubModel):
return new
def _sql_save(self, execute, data=None):
def _db_save(self, execute, data=None):
ind = data["ind"]
execute(
@@ -117,8 +121,8 @@ class Data(SQLSubModel):
"initial_conditions(ind, name, comment, kp, " +
"discharge, height, reach) " +
"VALUES (" +
f"{ind}, '{self._sql_format(self.name)}', " +
f"'{self._sql_format(self._comment)}', " +
f"{ind}, '{self._db_format(self.name)}', " +
f"'{self._db_format(self._comment)}', " +
f"{self._kp}, {self._discharge}, {self._height}, " +
f"{self._reach.id}" +
")"
@@ -226,21 +230,21 @@ class InitialConditions(SQLSubModel):
self._data = []
@classmethod
def _sql_create(cls, execute):
def _db_create(cls, execute):
return cls._create_submodel(execute)
@classmethod
def _sql_update(cls, execute, version):
def _db_update(cls, execute, version):
return cls._update_submodel(execute, version)
@classmethod
def _sql_load(cls, execute, data=None):
def _db_load(cls, execute, data=None):
new = cls(
reach=data["reach"],
status=data["status"]
)
new._data = Data._sql_load(
new._data = Data._db_load(
execute,
data=data
)
@@ -248,13 +252,13 @@ class InitialConditions(SQLSubModel):
if new._data is not None:
yield new
def _sql_save(self, execute, data=None):
def _db_save(self, execute, data=None):
ok = True
ind = 0
for d in self._data:
data["ind"] = ind
ok &= d._sql_save(execute, data)
ok &= d._db_save(execute, data)
ind += 1
return ok
@@ -355,40 +359,82 @@ class InitialConditions(SQLSubModel):
def generate_growing_constante_height(self, height: float):
self._data = []
profiles = self._reach.reach.profiles
profiles = self._reach.reach.profiles.copy()
self._sort_by_z_and_kp(profiles)
prev = None
incline = self._reach.reach.get_incline_median_mean()
logger.debug(f"incline = {incline}")
previous_elevation = -99999.99
for profile in profiles:
width = profile.width_approximation()
strickler = 25
discharge = (
((width * 0.8)
* strickler
* (height ** (5/3))
* (abs(incline) ** (0.5)))
)
elevation= max(
profile.z_min() + height,
previous_elevation
)
logger.debug(f"({profile.kp}):")
logger.debug(f" width = {width}")
logger.debug(f" strickler = {strickler}")
logger.debug(f" discharge = {discharge}")
new = Data(reach=self._reach, status=self._status)
new["kp"] = profile.kp
new["discharge"] = discharge
if prev is None:
new["elevation"] = profile.z_min() + height
else:
new["elevation"] = max(
profile.z_min() + height,
prev["elevation"]
)
new["elevation"] = elevation
self._data.append(new)
prev = new
previous_elevation = elevation
is_reverse = False
if profiles[0].kp > profiles[-1].kp:
is_reverse = True
self._data.sort(
reverse=not is_reverse,
key=lambda d: d['kp']
)
def generate_discharge(self, discharge: float):
self._new = []
self._data = []
for d in self._data:
n = d.copy()
n['discharge'] = discharge
self._new.append(n)
self._generate_height_estimation_from_discharge(
discharge
)
self._data = self._new
def _generate_height_estimation_from_discharge(self, discharge: float):
profiles = self._reach.reach.profiles.copy()
self._sort_by_z_and_kp(profiles)
previous_elevation = -99999.99
incline = self._reach.reach.get_incline_median_mean()
logger.debug(f"incline = {incline}")
for profile in profiles:
width = profile.width_approximation()
strickler = 25
height = (
discharge
/
((width * 0.8) * strickler * (abs(incline) ** (0.5)))
) ** (0.6)
elevation= max(
profile.z_min() + height,
previous_elevation
)
logger.debug(f"({profile.kp}):")
logger.debug(f" width = {width}")
logger.debug(f" strickler = {strickler}")
logger.debug(f" hieght = {height}")
new = Data(reach=self._reach, status=self._status)
new["kp"] = profile.kp
new["discharge"] = discharge
new["elevation"] = elevation
previous_elevation = elevation
self._data.append(new)
@@ -17,12 +17,16 @@
# -*- coding: utf-8 -*-
import types
import logging
from copy import copy
from tools import trace, timer
from Model.Tools.PamhyrDict import PamhyrModelDict
from Model.InitialConditions.InitialConditions import InitialConditions
logger = logging.getLogger()
class InitialConditionsDict(PamhyrModelDict):
_sub_classes = [
@@ -30,13 +34,13 @@ class InitialConditionsDict(PamhyrModelDict):
]
@classmethod
def _sql_load(cls, execute, data=None):
def _db_load(cls, execute, data=None):
new = cls(status=data["status"])
for reach in data["edges"]:
data["reach"] = reach
ic = InitialConditions._sql_load(
ic = InitialConditions._db_load(
execute,
data=data
)
@@ -46,16 +50,24 @@ class InitialConditionsDict(PamhyrModelDict):
return new
def _sql_save(self, execute, data=None):
def _db_save(self, execute, data=None):
ok = True
if data is None:
data = {}
execute("DELETE FROM initial_conditions")
for reach in self._dict:
ics = self._dict
for reach in ics:
data["reach"] = reach
ok &= self._dict[reach]._sql_save(execute, data)
v = self._dict[reach]
if isinstance(v, types.GeneratorType):
self._dict[reach] = list(v)[0]
execute(
"DELETE FROM initial_conditions " +
f"WHERE reach = '{reach.id}'"
)
ok &= self._dict[reach]._db_save(execute, data)
return ok
@@ -53,7 +53,7 @@ class LateralContribution(SQLSubModel):
LateralContribution._id_cnt + 1, self.id)
@classmethod
def _sql_create(cls, execute):
def _db_create(cls, execute):
execute("""
CREATE TABLE lateral_contribution(
id INTEGER NOT NULL PRIMARY KEY,
@@ -81,7 +81,7 @@ class LateralContribution(SQLSubModel):
return cls._create_submodel(execute)
@classmethod
def _sql_update(cls, execute, version):
def _db_update(cls, execute, version):
return True
@classmethod
@@ -100,7 +100,7 @@ class LateralContribution(SQLSubModel):
return res
@classmethod
def _sql_load(cls, execute, data=None):
def _db_load(cls, execute, data=None):
new = []
tab = data["tab"]
@@ -142,7 +142,7 @@ class LateralContribution(SQLSubModel):
return new
def _sql_save(self, execute, data=None):
def _db_save(self, execute, data=None):
tab = data["tab"]
execute(f"DELETE FROM lateral_contribution WHERE id = {self.id}")
@@ -157,8 +157,8 @@ class LateralContribution(SQLSubModel):
"lateral_contribution(id, name, type, tab, " +
"edge, begin_kp, end_kp) " +
"VALUES (" +
f"{self.id}, '{self._sql_format(self._name)}', " +
f"'{self._sql_format(self._type)}', '{tab}', {edge}, " +
f"{self.id}, '{self._db_format(self._name)}', " +
f"'{self._db_format(self._type)}', '{tab}', {edge}, " +
f"{self._begin_kp}, {self._end_kp}" +
")"
)
@@ -166,8 +166,8 @@ class LateralContribution(SQLSubModel):
ind = 0
for d in self._data:
data0 = self._sql_format(str(d[0]))
data1 = self._sql_format(str(d[1]))
data0 = self._db_format(str(d[0]))
data1 = self._db_format(str(d[1]))
sql = (
"INSERT INTO " +
@@ -188,8 +188,13 @@ class LateralContribution(SQLSubModel):
@classmethod
def time_convert(cls, data):
if data is str and data.count(":") == 3:
return old_pamhyr_date_to_timestamp(data)
if type(data) is str:
if data.count(":") == 3:
return old_pamhyr_date_to_timestamp(data)
if data.count(":") == 2:
return old_pamhyr_date_to_timestamp("00:" + data)
if data.count(".") == 1:
return round(float(data))
return int(data)
@@ -35,7 +35,7 @@ class LateralContributionList(PamhyrModelListWithTab):
]
@classmethod
def _sql_load(cls, execute, data=None):
def _db_load(cls, execute, data=None):
new = cls(status=data['status'])
if data is None:
@@ -43,13 +43,13 @@ class LateralContributionList(PamhyrModelListWithTab):
for tab in new._tabs:
data["tab"] = tab
new._tabs[tab] = LateralContribution._sql_load(
new._tabs[tab] = LateralContribution._db_load(
execute, data
)
return new
def _sql_save(self, execute, data=None):
def _db_save(self, execute, data=None):
execute("DELETE FROM lateral_contribution")
if data is None:
@@ -58,7 +58,7 @@ class LateralContributionList(PamhyrModelListWithTab):
for tab in self._tabs:
data["tab"] = tab
for lc in self._tabs[tab]:
lc._sql_save(execute, data=data)
lc._db_save(execute, data=data)
return True
+268
View File
@@ -0,0 +1,268 @@
# Reservoir.py -- Pamhyr
# Copyright (C) 2023 INRAE
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# -*- coding: utf-8 -*-
import logging
from Model.Tools.PamhyrDB import SQLSubModel
logger = logging.getLogger()
class Reservoir(SQLSubModel):
_sub_classes = []
_id_cnt = 0
def __init__(self, id: int = -1, name: str = "",
status=None):
super(Reservoir, self).__init__()
self._status = status
if id == -1:
self.id = Reservoir._id_cnt
else:
self.id = id
self._name = name
self._node = None
self._data = []
Reservoir._id_cnt = max(Reservoir._id_cnt + 1, self.id)
@classmethod
def _db_create(cls, execute):
execute("""
CREATE TABLE reservoir(
id INTEGER NOT NULL PRIMARY KEY,
name TEXT NOT NULL,
node INTEGER,
FOREIGN KEY(node) REFERENCES river_node(id)
)
""")
execute("""
CREATE TABLE reservoir_data(
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
ind INTEGER NOT NULL,
elevation REAL NOT NULL,
surface REAL NOT NULL,
reservoir INTEGER,
FOREIGN KEY(reservoir) REFERENCES reservoir(id)
)
""")
return cls._create_submodel(execute)
@classmethod
def _db_update(cls, execute, version):
major, minor, release = version.strip().split(".")
if major == minor == "0":
if int(release) < 5:
cls._db_create(execute)
return True
@classmethod
def _db_load(cls, execute, data=None):
new = []
table = execute(
"SELECT id, name, node " +
"FROM reservoir "
)
for row in table:
id = row[0]
name = row[1]
node_id = row[2]
new_reservoir = cls(id, name, status=data["status"])
new_reservoir._node = None
if node_id != -1:
new_reservoir._node = next(filter(lambda n: n.id == node_id, data["nodes"]))
new_data = []
table = execute(
"SELECT elevation, surface " +
"FROM reservoir_data " +
f"WHERE reservoir = {id} " +
"ORDER BY ind ASC"
)
for t in table:
new_data.append((t[0], t[1]))
new_reservoir._data = new_data
new.append(new_reservoir)
return new
def _db_save(self, execute, data=None):
execute(f"DELETE FROM reservoir WHERE id = {self.id}")
execute(f"DELETE FROM reservoir_data WHERE reservoir = {self.id}")
node_id = -1
if self._node is not None:
node_id = self._node.id
sql = (
"INSERT INTO " +
"reservoir(id, name, node) " +
"VALUES (" +
f"{self.id}, '{self._db_format(self._name)}', " +
f"{node_id}" +
")"
)
execute(sql)
ind = 0
for d in self._data:
sql = (
"INSERT INTO " +
"reservoir_data(ind, elevation, surface, reservoir) " +
f"VALUES ({ind}, '{d[0]}', {d[1]}, {self.id})"
)
execute(sql)
ind += 1
return True
def __len__(self):
return len(self._data)
@property
def name(self):
return self._name
@name.setter
def name(self, name):
self._name = name
self._status.modified()
@property
def node(self):
return self._node
@node.setter
def node(self, node):
self._node = node
self._status.modified()
def has_node(self):
return self._node is not None
@property
def data(self):
return self._data.copy()
@property
def _default_elevation(self):
return 0.0
@property
def _default_surface(self):
return 0.0
def is_define(self):
return len(self._data) != 0
def new_from_data(self, data):
try:
new_0 = float(data[0])
new_1 = float(data[1])
except Exception as e:
logger.error(e)
new_0 = None
new_1 = None
return (new_0, new_1)
def add(self, index: int):
value = (self._default_elevation, self._default_surface)
self._data.insert(index, value)
self._status.modified()
return value
def insert(self, index: int, value):
self._data.insert(index, value)
self._status.modified()
def delete_i(self, indexes):
self._data = list(
map(
lambda e: e[1],
filter(
lambda e: e[0] not in indexes,
enumerate(self.data)
)
)
)
self._status.modified()
def delete(self, els):
self._data = list(
filter(
lambda e: e not in els,
self.data
)
)
self._status.modified()
def sort(self, _reverse=False, key=None):
if key is None:
self._data.sort(reverse=_reverse)
else:
self._data.sort(reverse=_reverse, key=key)
self._status.modified()
def get_i(self, index):
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, column, value):
v = list(self._data[index])
v[column] = value
self._data[index] = tuple(v)
self._status.modified()
def set_i_elevation(self, index: int, value):
self._set_i_c_v(index, 0, value)
def set_i_surface(self, index: int, value):
self._set_i_c_v(index, 1, value)
def move_up(self, index):
if index < len(self):
next = index - 1
d = self._data
d[index], d[next] = d[next], d[index]
self._status.modified()
def move_down(self, index):
if index >= 0:
prev = index + 1
d = self._data
d[index], d[prev] = d[prev], d[index]
self._status.modified()
+87
View File
@@ -0,0 +1,87 @@
# ReservoirList.py -- Pamhyr
# Copyright (C) 2023 INRAE
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# -*- coding: utf-8 -*-
from copy import copy
from tools import trace, timer
from Model.Tools.PamhyrList import PamhyrModelList
from Model.Reservoir.Reservoir import Reservoir
class ReservoirList(PamhyrModelList):
_sub_classes = [
Reservoir,
]
@classmethod
def _db_load(cls, execute, data=None):
new = cls(status=data['status'])
new._lst = Reservoir._db_load(
execute, data
)
return new
def _db_save(self, execute, data=None):
execute("DELETE FROM reservoir")
execute("DELETE FROM reservoir_data")
if data is None:
data = {}
for reservoir in self._lst:
reservoir._db_save(execute, data=data)
return True
def new(self, index):
r = Reservoir(status=self._status)
self._lst.insert(index, r)
self._status.modified()
return r
def __copy__(self):
new = ReservoirList()
new._lst = self._lst.copy()
return new
def __deepcopy__(self):
new = ReservoirList()
new._lst = self._lst.deepcopy()
return new
def copy(self):
return copy(self)
def get_assoc_to_node(self, node):
assoc = list(
filter(
lambda i: i.node is node,
self._lst
)
)
if len(assoc) > 0:
return assoc[0]
return None
+39 -25
View File
@@ -36,6 +36,7 @@ from Model.Stricklers.StricklersList import StricklersList
from Model.Friction.FrictionList import FrictionList
from Model.SolverParameters.SolverParametersList import SolverParametersList
from Model.SedimentLayer.SedimentLayerList import SedimentLayerList
from Model.Reservoir.ReservoirList import ReservoirList
from Solver.Solvers import solver_type_list
@@ -55,7 +56,7 @@ class RiverNode(Node, SQLSubModel):
self._locker = None
@classmethod
def _sql_create(cls, execute):
def _db_create(cls, execute):
execute("""
CREATE TABLE river_node(
id INTEGER NOT NULL PRIMARY KEY,
@@ -69,11 +70,11 @@ class RiverNode(Node, SQLSubModel):
return True
@classmethod
def _sql_update(cls, execute, version):
def _db_update(cls, execute, version):
return True
@classmethod
def _sql_load(cls, execute, data=None):
def _db_load(cls, execute, data=None):
nodes = []
table = execute("SELECT id, name, x, y FROM river_node")
@@ -85,10 +86,10 @@ class RiverNode(Node, SQLSubModel):
return nodes
def _sql_save(self, execute, data=None):
def _db_save(self, execute, data=None):
sql = (
"INSERT OR REPLACE INTO river_node(id, name, x, y) VALUES (" +
f"{self.id}, '{self._sql_format(self.name)}', " +
f"{self.id}, '{self._db_format(self.name)}', " +
f"{self.x}, {self.y}" +
")"
)
@@ -125,7 +126,7 @@ class RiverReach(Edge, SQLSubModel):
self._frictions = FrictionList(status=self._status)
@classmethod
def _sql_create(cls, execute):
def _db_create(cls, execute):
execute("""
CREATE TABLE river_reach(
id INTEGER NOT NULL PRIMARY KEY,
@@ -142,11 +143,11 @@ class RiverReach(Edge, SQLSubModel):
return True
@classmethod
def _sql_update(cls, execute, version):
def _db_update(cls, execute, version):
return cls._update_submodel(execute, version)
@classmethod
def _sql_load(cls, execute, data=None):
def _db_load(cls, execute, data=None):
reachs = []
if data is None:
@@ -170,21 +171,21 @@ class RiverReach(Edge, SQLSubModel):
data["reach"] = id
data["parent"] = new
new._reach = Reach._sql_load(execute, data)
new._reach = Reach._db_load(execute, data)
new._frictions = FrictionList._sql_load(execute, data)
new._frictions = FrictionList._db_load(execute, data)
reachs.append(new)
return reachs
def _sql_save(self, execute, data=None):
def _db_save(self, execute, data=None):
sql = (
"INSERT OR REPLACE INTO " +
"river_reach(id, name, enable, node1, node2) " +
"VALUES (" +
f"{self.id}, '{self._sql_format(self._name)}', " +
f"{self._sql_format(self.is_enable())},"
f"{self.id}, '{self._db_format(self._name)}', " +
f"{self._db_format(self.is_enable())},"
f"{self.node1.id}, {self.node2.id}" +
")"
)
@@ -217,6 +218,7 @@ class River(Graph, SQLSubModel):
StricklersList,
SolverParametersList,
SedimentLayerList,
ReservoirList,
]
def __init__(self, status=None):
@@ -234,81 +236,89 @@ class River(Graph, SQLSubModel):
self._stricklers = StricklersList(status=self._status)
self._parameters = {}
self._sediment_layers = SedimentLayerList(status=self._status)
self._reservoir = ReservoirList(status=self._status)
@classmethod
def _sql_create(cls, execute):
def _db_create(cls, execute):
cls._create_submodel(execute)
return True
@classmethod
def _sql_update(cls, execute, version):
def _db_update(cls, execute, version):
cls._update_submodel(execute, version)
return True
@classmethod
def _sql_load(cls, execute, data=None):
def _db_load(cls, execute, data=None):
new = cls(data["status"])
# Stricklers (Stricklers is load in first because it's needed
# for reachs)
new._stricklers = StricklersList._sql_load(
new._stricklers = StricklersList._db_load(
execute,
data
)
data["stricklers"] = new._stricklers
# Initial conditions
new._sediment_layers = SedimentLayerList._sql_load(
new._sediment_layers = SedimentLayerList._db_load(
execute,
data
)
data["sediment_layers_list"] = new._sediment_layers
# Network
new._nodes = RiverNode._sql_load(
new._nodes = RiverNode._db_load(
execute,
data
)
data["nodes"] = new.nodes()
new._edges = RiverReach._sql_load(
new._edges = RiverReach._db_load(
execute,
data
)
data["edges"] = new.edges()
# Boundary Condition
new._boundary_condition = BoundaryConditionList._sql_load(
new._boundary_condition = BoundaryConditionList._db_load(
execute,
data
)
# Lateral Contribution
new._lateral_contribution = LateralContributionList._sql_load(
new._lateral_contribution = LateralContributionList._db_load(
execute,
data
)
# Initial conditions
new._initial_conditions = InitialConditionsDict._sql_load(
new._initial_conditions = InitialConditionsDict._db_load(
execute,
data
)
# Reservoir
new._reservoir = ReservoirList._db_load(
execute,
data
)
# Parameters
new._parameters = SolverParametersList._sql_load(
new._parameters = SolverParametersList._db_load(
execute,
data
)
return new
def _sql_save(self, execute, data=None):
def _db_save(self, execute, data=None):
objs = (self._nodes + self._edges)
objs.append(self._boundary_condition)
objs.append(self._initial_conditions)
objs.append(self._lateral_contribution)
objs.append(self._sediment_layers)
objs.append(self._stricklers)
objs.append(self._reservoir)
for solver in self._parameters:
objs.append(self._parameters[solver])
@@ -349,6 +359,10 @@ class River(Graph, SQLSubModel):
return ret[0]
@property
def reservoir(self):
return self._reservoir
@property
def parameters(self):
return self._parameters
+16 -16
View File
@@ -84,7 +84,7 @@ class Layer(SQLSubModel):
self._critical_constraint = float(critical_constraint)
@classmethod
def _sql_create(cls, execute):
def _db_create(cls, execute):
execute("""
CREATE TABLE sedimentary_layer_layer(
id INTEGER NOT NULL PRIMARY KEY,
@@ -103,16 +103,16 @@ class Layer(SQLSubModel):
return cls._create_submodel(execute)
@classmethod
def _sql_update(cls, execute, version):
def _db_update(cls, execute, version):
major, minor, release = version.strip().split(".")
if major == minor == "0":
if int(release) < 2:
cls._sql_create(execute)
cls._db_create(execute)
return True
@classmethod
def _sql_load(cls, execute, data=None):
def _db_load(cls, execute, data=None):
new = []
sl = data["sl"]
@@ -141,7 +141,7 @@ class Layer(SQLSubModel):
return new
def _sql_save(self, execute, data=None):
def _db_save(self, execute, data=None):
ind = data["ind"]
sl = data["sl"]
@@ -150,8 +150,8 @@ class Layer(SQLSubModel):
"sedimentary_layer_layer(id, ind, name, type, height, " +
"d50, sigma, critical_constraint, sl) " +
"VALUES (" +
f"{self.id}, {ind}, '{self._sql_format(self._name)}', " +
f"'{self._sql_format(self._type)}', {self._height}, " +
f"{self.id}, {ind}, '{self._db_format(self._name)}', " +
f"'{self._db_format(self._type)}', {self._height}, " +
f"{self._d50}, {self._sigma}, {self._critical_constraint}, " +
f"{sl.id}" +
")"
@@ -224,7 +224,7 @@ class SedimentLayer(SQLSubModel):
self._comment = comment
@classmethod
def _sql_create(cls, execute):
def _db_create(cls, execute):
execute("""
CREATE TABLE sedimentary_layer(
id INTEGER NOT NULL PRIMARY KEY,
@@ -236,16 +236,16 @@ class SedimentLayer(SQLSubModel):
return cls._create_submodel(execute)
@classmethod
def _sql_update(cls, execute, version):
def _db_update(cls, execute, version):
major, minor, release = version.strip().split(".")
if major == minor == "0":
if int(release) < 2:
cls._sql_create(execute)
cls._db_create(execute)
return True
@classmethod
def _sql_load(cls, execute, data=None):
def _db_load(cls, execute, data=None):
new = []
table = execute(
@@ -262,20 +262,20 @@ class SedimentLayer(SQLSubModel):
)
data["sl"] = sl.id
sl._layers = Layer._sql_load(execute, data)
sl._layers = Layer._db_load(execute, data)
new.append(sl)
return new
def _sql_save(self, execute, data=None):
def _db_save(self, execute, data=None):
if data is None:
data = {}
sql = (
"INSERT INTO sedimentary_layer (id, name, comment) " +
f"VALUES ({self.id}, '{self._sql_format(self._name)}', " +
f"'{self._sql_format(self._comment)}')"
f"VALUES ({self.id}, '{self._db_format(self._name)}', " +
f"'{self._db_format(self._comment)}')"
)
execute(sql)
@@ -284,7 +284,7 @@ class SedimentLayer(SQLSubModel):
ind = 0
for layer in self._layers:
data["ind"] = ind
layer._sql_save(execute, data)
layer._db_save(execute, data)
ind += 1
return True
+4 -4
View File
@@ -11,14 +11,14 @@ class SedimentLayerList(PamhyrModelList):
_sub_classes = [SedimentLayer]
@classmethod
def _sql_load(cls, execute, data=None):
def _db_load(cls, execute, data=None):
new = cls(status=data["status"])
new._lst = SedimentLayer._sql_load(execute, data)
new._lst = SedimentLayer._db_load(execute, data)
return new
def _sql_save(self, execute, data=None):
def _db_save(self, execute, data=None):
ok = True
# Delete previous data
@@ -26,7 +26,7 @@ class SedimentLayerList(PamhyrModelList):
execute("DELETE FROM sedimentary_layer_layer")
for sl in self._lst:
ok &= sl._sql_save(execute, data)
ok &= sl._db_save(execute, data)
return ok
@@ -79,7 +79,7 @@ class SolverParametersList(PamhyrModelList):
)
@classmethod
def _sql_create(cls, execute):
def _db_create(cls, execute):
execute("""
CREATE TABLE solver_parameter(
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
@@ -93,7 +93,7 @@ class SolverParametersList(PamhyrModelList):
return cls._create_submodel(execute)
@classmethod
def _sql_update(cls, execute, version):
def _db_update(cls, execute, version):
major, minor, release = version.strip().split(".")
if major == minor == "0":
@@ -130,9 +130,9 @@ class SolverParametersList(PamhyrModelList):
"solver_parameter(ind, name, value, solver) " +
"VALUES (" +
f"{ind}, " +
f"'{cls._sql_format(name)}', " +
f"'{cls._sql_format(value)}', " +
f"'{cls._sql_format(solver)}'" +
f"'{cls._db_format(name)}', " +
f"'{cls._db_format(value)}', " +
f"'{cls._db_format(solver)}'" +
")"
)
@@ -164,7 +164,7 @@ class SolverParametersList(PamhyrModelList):
return cls._update_submodel(execute, version)
@classmethod
def _sql_load(cls, execute, data=None):
def _db_load(cls, execute, data=None):
status = data["status"]
solvers = execute("SELECT DISTINCT solver FROM solver_parameter")
new = {}
@@ -194,7 +194,7 @@ class SolverParametersList(PamhyrModelList):
return new
def _sql_save(self, execute, data=None):
def _db_save(self, execute, data=None):
t = self._solver._type
execute(
"DELETE FROM solver_parameter " +
@@ -208,9 +208,9 @@ class SolverParametersList(PamhyrModelList):
"solver_parameter(ind, name, value, solver) " +
"VALUES (" +
f"{ind}, " +
f"'{self._sql_format(param.name)}', " +
f"'{self._sql_format(param.value)}', " +
f"'{self._sql_format(t)}'" +
f"'{self._db_format(param.name)}', " +
f"'{self._db_format(param.value)}', " +
f"'{self._db_format(t)}'" +
")"
)
execute(sql)
+6 -6
View File
@@ -48,7 +48,7 @@ class Stricklers(SQLSubModel):
self._medium = medium
@classmethod
def _sql_create(cls, execute):
def _db_create(cls, execute):
execute("""
CREATE TABLE stricklers(
id INTEGER NOT NULL PRIMARY KEY,
@@ -62,11 +62,11 @@ class Stricklers(SQLSubModel):
return cls._create_submodel(execute)
@classmethod
def _sql_update(cls, execute, version):
def _db_update(cls, execute, version):
return cls._update_submodel(execute, version)
@classmethod
def _sql_load(cls, execute, data=None):
def _db_load(cls, execute, data=None):
stricklers = []
status = data["status"]
@@ -97,14 +97,14 @@ class Stricklers(SQLSubModel):
return stricklers
def _sql_save(self, execute, data=None):
def _db_save(self, execute, data=None):
sql = (
"INSERT INTO " +
"stricklers(id, name, comment, minor, medium) " +
"VALUES (" +
f"{self.id}, " +
f"'{self._sql_format(self.name)}', " +
f"'{self._sql_format(self.comment)}', " +
f"'{self._db_format(self.name)}', " +
f"'{self._db_format(self.comment)}', " +
f"{float(self.minor)}, {float(self.medium)}" +
")"
)
+5 -5
View File
@@ -29,25 +29,25 @@ class StricklersList(PamhyrModelList):
]
@classmethod
def _sql_create(cls, execute):
def _db_create(cls, execute):
return cls._create_submodel(execute)
@classmethod
def _sql_update(cls, execute, version):
def _db_update(cls, execute, version):
return cls._update_submodel(execute, version)
@classmethod
def _sql_load(cls, execute, data=None):
def _db_load(cls, execute, data=None):
new = cls(status=data["status"])
new._lst = Stricklers._sql_load(
new._lst = Stricklers._db_load(
execute,
data=data
)
return new
def _sql_save(self, execute, data=None):
def _db_save(self, execute, data=None):
execute("DELETE FROM stricklers")
objs = self._lst
+12 -5
View File
@@ -41,7 +41,7 @@ class Study(SQLModel):
def __init__(self, filename=None, init_new=True):
# Metadata
self._version = "0.0.4"
self._version = "0.0.5"
self.creation_date = datetime.now()
self.last_modification_date = datetime.now()
self.last_save_date = datetime.now()
@@ -177,13 +177,20 @@ class Study(SQLModel):
# SQL #
#######
def _db_insert_into_info(self, key, value, commit=False):
self.execute(
"INSERT INTO info VALUES " +
f"('{key}', '{self._db_format(value)}')",
commit=commit
)
def _create(self):
# Info (metadata)
self.execute(
"CREATE TABLE info(key TEXT NOT NULL UNIQUE, value TEXT NOT NULL)")
self.execute(
"INSERT INTO info VALUES ('version', " +
f"'{self._sql_format(self._version)}')",
f"'{self._db_format(self._version)}')",
commit=True
)
self.execute("INSERT INTO info VALUES ('name', '')")
@@ -248,7 +255,7 @@ class Study(SQLModel):
)
# Load river data
new._river = River._sql_load(
new._river = River._db_load(
lambda sql: new.execute(
sql,
fetch_one=False,
@@ -262,11 +269,11 @@ class Study(SQLModel):
def _save(self):
self.execute(
f"UPDATE info SET " +
f"value='{self._sql_format(self.name)}' WHERE key='name'"
f"value='{self._db_format(self.name)}' WHERE key='name'"
)
self.execute(
f"UPDATE info SET " +
f"value='{self._sql_format(self.description)}' " +
f"value='{self._db_format(self.description)}' " +
"WHERE key='description'"
)
self.execute(
+17 -17
View File
@@ -61,7 +61,7 @@ class SQLModel(SQL):
)
for cls in self._sub_classes:
requests = cls._sql_create(fn)
requests = cls._db_create(fn)
self.commit()
return True
@@ -78,7 +78,7 @@ class SQLModel(SQL):
ok = True
for cls in self._sub_classes:
ok &= cls._sql_update(fn, version)
ok &= cls._db_update(fn, version)
self.commit()
return ok
@@ -95,7 +95,7 @@ class SQLModel(SQL):
ok = True
for obj in objs:
ok &= obj._sql_save(fn)
ok &= obj._db_save(fn)
self.commit()
return ok
@@ -113,21 +113,21 @@ class SQLModel(SQL):
class SQLSubModel(object):
_sub_classes = []
def _sql_format(self, value):
def _db_format(self, value):
# Replace ''' by '&#39;' to preserve SQL injection
if value is str:
if type(value) is str:
value = value.replace("'", "&#39;")
elif value is bool:
elif type(value) is bool:
value = 'TRUE' if value else 'FALSE'
return value
@classmethod
def _create_submodel(cls, execute):
for sc in cls._sub_classes:
sc._sql_create(execute)
sc._db_create(execute)
@classmethod
def _sql_create(cls, execute):
def _db_create(cls, execute):
"""Create data base scheme
Args:
@@ -136,15 +136,15 @@ class SQLSubModel(object):
Returns:
Return true, otherelse false if an issue appear
"""
raise NotImplementedMethodeError(cls, cls._sql_create)
raise NotImplementedMethodeError(cls, cls._db_create)
@classmethod
def _update_submodel(cls, execute, version):
for sc in cls._sub_classes:
sc._sql_update(execute, version)
sc._db_update(execute, version)
@classmethod
def _sql_update(cls, execute, version):
def _db_update(cls, execute, version):
"""Update data base scheme
Args:
@@ -154,10 +154,10 @@ class SQLSubModel(object):
Returns:
Return true, otherelse false if an issue appear
"""
raise NotImplementedMethodeError(cls, cls._sql_update)
raise NotImplementedMethodeError(cls, cls._db_update)
@classmethod
def _sql_load(cls, execute, data=None):
def _db_load(cls, execute, data=None):
"""Load instance of this class from SQL data base
Args:
@@ -167,13 +167,13 @@ class SQLSubModel(object):
Returns:
Return new instance of class
"""
raise NotImplementedMethodeError(cls, cls._sql_load)
raise NotImplementedMethodeError(cls, cls._db_load)
def _save_submodel(self, execute, objs, data=None):
for o in objs:
o._sql_save(execute, data=data)
o._db_save(execute, data=data)
def _sql_save(self, execute, data=None):
def _db_save(self, execute, data=None):
"""Save class data to data base
Args:
@@ -184,4 +184,4 @@ class SQLSubModel(object):
Return true, otherelse false if an issue appear during
save
"""
raise NotImplementedMethodeError(self, self._sql_save)
raise NotImplementedMethodeError(self, self._db_save)
+13 -7
View File
@@ -16,6 +16,7 @@
# -*- coding: utf-8 -*-
import types
import logging
from copy import copy
@@ -38,19 +39,19 @@ class PamhyrModelDict(SQLSubModel):
self._dict = {}
@classmethod
def _sql_create(cls, execute):
def _db_create(cls, execute):
return cls._create_submodel(execute)
@classmethod
def _sql_update(cls, execute, version):
def _db_update(cls, execute, version):
return cls._update_submodel(execute, version)
@classmethod
def _sql_load(cls, execute, data=None):
raise NotImplementedMethodeError(cls, cls._sql_load)
def _db_load(cls, execute, data=None):
raise NotImplementedMethodeError(cls, cls._db_load)
def _sql_save(self, execute, data=None):
raise NotImplementedMethodeError(self, self._sql_save)
def _db_save(self, execute, data=None):
raise NotImplementedMethodeError(self, self._db_save)
def __len__(self):
return len(self._dict)
@@ -64,7 +65,12 @@ class PamhyrModelDict(SQLSubModel):
def get(self, key):
if key in self._dict:
return self._dict[key]
v = self._dict[key]
if type(v) == types.GeneratorType:
return list(v)
return v
new = self.new(key)
self.set(key, new)
+12 -12
View File
@@ -42,19 +42,19 @@ class PamhyrModelList(SQLSubModel):
#######
@classmethod
def _sql_create(cls, execute):
def _db_create(cls, execute):
return cls._create_submodel(execute)
@classmethod
def _sql_update(cls, execute, version):
def _db_update(cls, execute, version):
return cls._update_submodel(execute, version)
@classmethod
def _sql_load(cls, execute, data=None):
raise NotImplementedMethodeError(cls, cls._sql_load)
def _db_load(cls, execute, data=None):
raise NotImplementedMethodeError(cls, cls._db_load)
def _sql_save(self, execute, data=None):
raise NotImplementedMethodeError(self, self._sql_save)
def _db_save(self, execute, data=None):
raise NotImplementedMethodeError(self, self._db_save)
################
# MODEL METHOD #
@@ -156,19 +156,19 @@ class PamhyrModelListWithTab(SQLSubModel):
#######
@classmethod
def _sql_create(cls, execute):
def _db_create(cls, execute):
return cls._create_submodel(execute)
@classmethod
def _sql_update(cls, execute, version):
def _db_update(cls, execute, version):
return cls._update_submodel(execute, version)
@classmethod
def _sql_load(cls, execute, data=None):
raise NotImplementedMethodeError(cls, cls._sql_load)
def _db_load(cls, execute, data=None):
raise NotImplementedMethodeError(cls, cls._db_load)
def _sql_save(self, execute, data=None):
raise NotImplementedMethodeError(self, self._sql_save)
def _db_save(self, execute, data=None):
raise NotImplementedMethodeError(self, self._db_save)
################
# MODEL METHOD #
+341
View File
@@ -0,0 +1,341 @@
#!/bin/env python3
import sys
import importlib.util as imp
import os
import datetime as dt
import lxml.etree as ET
def mage_to_mascaret(filename):
# rep file
with open(filename, encoding = "ISO-8859-1", mode ="r") as rep:
lines = rep.readlines()
for line in lines:
l = line.split()
if (l[0] == 'PAR'):
parfile = l[1]
elif (l[0] == 'NET'):
netfile = l[1]
elif (l[0] == 'HYD'):
hydfile = l[1]
elif (l[0] == 'RUG'):
rugfile = l[1]
elif (l[0] == 'LIM'):
limfile = l[1]
elif (l[0] == 'INI'):
inifile = l[1]
# net file
with open(netfile, encoding = "ISO-8859-1", mode ="r") as net:
lines = net.readlines()
for line in lines:
if (line[0] != '*' and line[0] != '$'):
l = line.split()
biefname = l[0]
stfile = l[3]
break # on n'utilise que le premier bief pour le moment
# geometry file
print("geometrie fie : "+stfile)
wb.init_bief_from_geo_file(stfile, 0, 0)
wb.set_bief_name(biefname)
nb_sect = wb.get_nb_sections()
print("nb sections: "+str(nb_sect))
# hydrogramme file
with open(hydfile, encoding = "ISO-8859-1", mode ="r") as hyd:
HYD = []
lines = hyd.readlines()
for line in lines:
if (line[0] != '*' and line[0] != '$'):
HYD.append([x for x in line.split()])
# friction file
with open(rugfile, encoding = "ISO-8859-1", mode ="r") as lim:
RUG = []
lines = lim.readlines()
for line in lines:
if (line[0] != '*' and line[0] != '$'):
RUG.append([x for x in line.split()])
# tarage file
with open(limfile, encoding = "ISO-8859-1", mode ="r") as tar:
LIM = []
lines = tar.readlines()
for line in lines:
if (line[0] != '*' and line[0] != '$'):
LIM.append([x for x in line.split()])
# cond init file
with open(inifile, encoding = "ISO-8859-1", mode ="r") as ini:
INI = []
lines = ini.readlines()
for line in lines:
if (line[0] != '*' and line[0] != '$'):
INI.append([x for x in line.split()])
# PAR file
with open(parfile, encoding = "ISO-8859-1", mode ="r") as par:
PAR = {}
lines = par.readlines()
for line in lines:
l = line.split()
if l[0] == 'init_time':
t = l[1].split(":")
if len(t)>1:
PAR['init_time'] = int(t[0])*24*3600+int(t[1])*3600+int(t[2])*60+int(t[3])
else:
PAR['init_time'] = int(t[0])*60
elif l[0] == 'final_time':
t = l[1].split(":")
if len(t)>1:
PAR['final_time'] = int(t[0])*24*3600+int(t[1])*3600+int(t[2])*60+int(t[3])
else:
PAR['final_time'] = int(t[0])*60
elif l[0] == 'timestep':
PAR['timestep'] = float(l[1])
elif l[0] == 'timestep_bin':
PAR['timestep_bin'] = float(l[1])
elif l[0] == 'timestep_tra':
PAR['timestep_tra'] = float(l[1])
elif l[0] == 'min_height':
PAR['min_height'] = float(l[1])
#os.chdir(current_dir)
with open("hydrogramme.loi", mode ="w") as hyd:
hyd.write(" S\n")
for line in HYD:
hyd.write(" "+line[0]+" "+line[1]+"\n")
if float(HYD[-1][0]) < PAR['final_time']:
hyd.write(" "+str(PAR['final_time'])+" "+str(float(HYD[-1][1]))+"\n")
with open("limnigramme.loi", mode ="w") as lim:
lim.write(" S\n")
for line in LIM:
lim.write(" "+line[0]+" "+line[1]+"\n")
if float(LIM[-1][0]) < PAR['final_time']:
lim.write(" "+str(PAR['final_time'])+" "+str(float(LIM[-1][1]))+"\n")
with open("init.lig", mode ="w") as lig:
lig.write("RESULTATS CALCUL,DATE : "+dt.datetime.now().strftime("%d/%m/%y %H:%M"+"\n"))
lig.write("FICHIER RESULTAT MASCARET\n")
lig.write("-"*71+"\n")
lig.write(" IMAX = "+f"{len(INI):4d}"+" NBBIEF= 1\n")
lig.write(" I1,I2 = 1 "+str(len(INI))+"\n")
lig.write(" X\n")
for i in range(int(len(INI)/5)+1):
lig.write(" "+"".join([f"{float(INI[j][4]):12.2f}" for j in range(i*5, min(i*5+5, len(INI)))])+"\n")
lig.write(" Z\n")
for i in range(int(len(INI)/5)+1):
lig.write(" "+"".join([f"{float(INI[j][3]):12.3f}" for j in range(i*5, min(i*5+5, len(INI)))])+"\n")
lig.write(" Q\n")
for i in range(int(len(INI)/5)+1):
lig.write(" "+"".join([f"{float(INI[j][2]):12.3f}" for j in range(i*5, min(i*5+5, len(INI)))])+"\n")
lig.write(" FIN\n")
#os.remove("geometrie")
wb.output_bief_mascaret("geometrie")
pk = [wb.get_pk_section(i+1) for i in range(nb_sect)]
print(pk)
print(max(pk))
print(min(pk))
out_name = 'FichierCas.xcas'
root = ET.Element('fichierCas')
pc = ET.SubElement(root, 'parametresCas')
pg = ET.SubElement(pc, 'parametresGeneraux')
ET.SubElement(pg, 'versionCode').text='3'
ET.SubElement(pg, 'code').text = '3'
ET.SubElement(pg, 'fichMotsCles').text = out_name
ET.SubElement(pg, 'dictionaire').text='dico.txt'
ET.SubElement(pg, 'progPrincipal').text='princi.f'
ET.SubElement(pg, 'sauveModele').text='false'
ET.SubElement(pg, 'fichSauvModele').text='mascaret_exp.tmp'
ET.SubElement(pg, 'validationCode').text='false'
ET.SubElement(pg, 'typeValidation').text='1'
ET.SubElement(pg, 'presenceCasiers').text='false'
b = ET.SubElement(pg, 'bibliotheques')
ET.SubElement(b, 'bibliotheque').text='mascaretV5P1.a damoV3P0.a'
pmp = ET.SubElement(pc, 'parametresModelePhysique')
ET.SubElement(pmp, 'perteChargeConf').text='false'
ET.SubElement(pmp, 'compositionLits').text='1'
ET.SubElement(pmp, 'conservFrotVertical').text='false'
ET.SubElement(pmp, 'elevCoteArrivFront').text='0.05'
ET.SubElement(pmp, 'interpolLinStrickler').text='false'
d = ET.SubElement(pmp, 'debordement')
ET.SubElement(d, 'litMajeur').text='false'
ET.SubElement(d, 'zoneStock').text='false'
pn = ET.SubElement(pc, 'parametresNumeriques')
ET.SubElement(pn, 'calcOndeSubmersion').text='false'
ET.SubElement(pn, 'decentrement').text='false'
ET.SubElement(pn, 'froudeLimCondLim').text='1000.0'
ET.SubElement(pn, 'traitImplicitFrot').text='false'
ET.SubElement(pn, 'hauteurEauMini').text=str(PAR['min_height'])
ET.SubElement(pn, 'implicitNoyauTrans').text='true'
ET.SubElement(pn, 'optimisNoyauTrans').text='false'
ET.SubElement(pn, 'perteChargeAutoElargissement').text='false'
ET.SubElement(pn, 'termesNonHydrostatiques').text='false'
ET.SubElement(pn, 'apportDebit').text='0'
ET.SubElement(pn, 'attenuationConvection').text='false'
pt = ET.SubElement(pc, 'parametresTemporels')
ET.SubElement(pt, 'pasTemps').text=str(PAR['timestep'])
ET.SubElement(pt, 'tempsInit').text=str(PAR['init_time'])
ET.SubElement(pt, 'critereArret').text='1'
ET.SubElement(pt, 'nbPasTemps').text='2000'
ET.SubElement(pt, 'tempsMax').text=str(PAR['final_time'])
ET.SubElement(pt, 'pasTempsVar').text='true'
ET.SubElement(pt, 'nbCourant').text='0.8'
ET.SubElement(pt, 'coteMax').text='0.0'
ET.SubElement(pt, 'abscisseControle').text='0.0'
ET.SubElement(pt, 'biefControle').text='1'
pgr = ET.SubElement(pc, 'parametresGeometrieReseau')
g = ET.SubElement(pgr, 'geometrie')
ET.SubElement(g, 'fichier').text='geometrie'
ET.SubElement(g, 'format').text='2'
ET.SubElement(g, 'profilsAbscAbsolu').text='true'
lb = ET.SubElement(pgr, 'listeBranches')
ET.SubElement(lb, 'nb').text='1'
ET.SubElement(lb, 'numeros').text='1'
ET.SubElement(lb, 'abscDebut').text=str(min(pk))
ET.SubElement(lb, 'abscFin').text=str(max(pk))
ET.SubElement(lb, 'numExtremDebut').text='1'
ET.SubElement(lb, 'numExtremFin').text='2'
ln = ET.SubElement(pgr, 'listeNoeuds')
ET.SubElement(ln, 'nb').text='0'
ET.SubElement(ln, 'noeuds')
el = ET.SubElement(pgr, 'extrLibres')
ET.SubElement(el, 'nb').text='2'
ET.SubElement(el, 'num').text='1 2'
ET.SubElement(el, 'numExtrem').text='1 2'
noms = ET.SubElement(el, 'noms')
ET.SubElement(noms, 'string').text='limite1'
ET.SubElement(noms, 'string').text='limite2'
ET.SubElement(el, 'typeCond').text='1 2'
ET.SubElement(el, 'numLoi').text='1 2'
pc2 = ET.SubElement(pc, 'parametresConfluents')
ET.SubElement(pc2, 'nbConfluents').text='0'
ET.SubElement(pc2, 'confluents')
pcm = ET.SubElement(pc, 'parametresPlanimetrageMaillage')
ET.SubElement(pcm, 'methodeMaillage').text='1'
planim = ET.SubElement(pcm, 'planim')
ET.SubElement(planim, 'nbPas').text='101'
ET.SubElement(planim, 'nbZones').text='1'
ET.SubElement(planim, 'valeursPas').text='0.1'
ET.SubElement(planim, 'num1erProf').text='1'
ET.SubElement(planim, 'numDerProf').text=str(nb_sect)
maillage = ET.SubElement(pcm, 'maillage')
ET.SubElement(maillage, 'modeSaisie').text='2'
ET.SubElement(maillage, 'sauvMaillage').text='false'
maillageClavier = ET.SubElement(maillage, 'maillageClavier')
ET.SubElement(maillageClavier, 'nbSections').text='0'
ET.SubElement(maillageClavier, 'nbPlages').text='1'
ET.SubElement(maillageClavier, 'num1erProfPlage').text='1'
ET.SubElement(maillageClavier, 'numDerProfPlage').text=str(nb_sect)
ET.SubElement(maillageClavier, 'pasEspacePlage').text='10.0'
ET.SubElement(maillageClavier, 'nbZones').text='0'
ps = ET.SubElement(pc, 'parametresSingularite')
ET.SubElement(ps, 'nbSeuils').text='0'
pad = ET.SubElement(pc, 'parametresApportDeversoirs')
pc3 = ET.SubElement(pc, 'parametresCalage')
frt = ET.SubElement(pc3, 'frottement')
ET.SubElement(frt, 'loi').text='1'
ET.SubElement(frt, 'nbZone').text=str(len(RUG))
ET.SubElement(frt, 'numBranche').text=' '.join([i[1] for i in RUG])
ET.SubElement(frt, 'absDebZone').text=' '.join([i[2] for i in RUG])
ET.SubElement(frt, 'absFinZone').text=' '.join([i[3] for i in RUG])
ET.SubElement(frt, 'coefLitMin').text=' '.join([i[4] for i in RUG])
ET.SubElement(frt, 'coefLitMaj').text=' '.join([i[5] for i in RUG])
zst = ET.SubElement(pc3, 'zoneStockage')
ET.SubElement(zst, 'nbProfils').text='0'
ET.SubElement(zst, 'numProfil').text='-0'
ET.SubElement(zst, 'limGauchLitMaj').text='-0'
ET.SubElement(zst, 'limDroitLitMaj').text='-0'
plh = ET.SubElement(pc, 'parametresLoisHydrauliques')
ET.SubElement(plh, 'nb').text='2'
lois = ET.SubElement(plh, 'lois')
spl = ET.SubElement(lois, 'structureParametresLoi')
ET.SubElement(spl, 'nom').text='loi_1_hydrogramme'
ET.SubElement(spl, 'type').text='1'
donnees = ET.SubElement(spl, 'donnees')
ET.SubElement(donnees, 'modeEntree').text='1'
ET.SubElement(donnees, 'fichier').text='hydrogramme.loi'
ET.SubElement(donnees, 'uniteTps').text='-0'
ET.SubElement(donnees, 'nbPoints').text='-0'
ET.SubElement(donnees, 'nbDebitsDifferents').text='-0'
spl2 = ET.SubElement(lois, 'structureParametresLoi')
ET.SubElement(spl2, 'nom').text='loi_2_limnigramme'
ET.SubElement(spl2, 'type').text='2'
donnees = ET.SubElement(spl2, 'donnees')
ET.SubElement(donnees, 'modeEntree').text='1'
ET.SubElement(donnees, 'fichier').text='limnigramme.loi'
ET.SubElement(donnees, 'uniteTps').text='-0'
ET.SubElement(donnees, 'nbPoints').text='-0'
ET.SubElement(donnees, 'nbDebitsDifferents').text='-0'
pci = ET.SubElement(pc, 'parametresConditionsInitiales')
re = ET.SubElement(pci, 'repriseEtude')
ET.SubElement(re, 'repriseCalcul').text='false'
le = ET.SubElement(pci, 'ligneEau')
ET.SubElement(le, 'LigEauInit').text='true'
ET.SubElement(le, 'modeEntree').text='1'
ET.SubElement(le, 'fichLigEau').text='init.lig'
ET.SubElement(le, 'formatFichLig').text='2'
ET.SubElement(le, 'nbPts').text='-0'
pir = ET.SubElement(pc, 'parametresImpressionResultats')
ET.SubElement(pir, 'titreCalcul').text='Etude hydraulique1d'
impression = ET.SubElement(pir, 'impression')
ET.SubElement(impression, 'impressionGeometrie').text='false'
ET.SubElement(impression, 'impressionPlanimetrage').text='false'
ET.SubElement(impression, 'impressionReseau').text='false'
ET.SubElement(impression, 'impressionLoiHydraulique').text='false'
ET.SubElement(impression, 'impressionligneEauInitiale').text='false'
ET.SubElement(impression, 'impressionCalcul').text='false'
ps = ET.SubElement(pir, 'pasStockage')
ET.SubElement(ps, 'premPasTpsStock').text='1'
ET.SubElement(ps, 'pasStock').text=str(int(PAR['timestep_bin']/PAR['timestep']))
ET.SubElement(ps, 'pasImpression').text=str(int(PAR['timestep_tra']/PAR['timestep']))
results = ET.SubElement(pir, 'resultats')
ET.SubElement(results, 'fichResultat').text='mascaret_exp.opt'
ET.SubElement(results, 'postProcesseur').text='2'
listing = ET.SubElement(pir, 'listing')
ET.SubElement(listing, 'fichListing').text='mascaret_exp.lis'
reprise = ET.SubElement(pir, 'fichReprise')
ET.SubElement(reprise, 'fichRepriseEcr').text='mascaret_exp.rep'
rub = ET.SubElement(pir, 'rubens')
ET.SubElement(rub, 'ecartInterBranch').text='1.0'
stockage = ET.SubElement(pir, 'stockage')
ET.SubElement(stockage, 'option').text='1'
ET.SubElement(stockage, 'nbSite').text='0'
pvc = ET.SubElement(pc, 'parametresVariablesCalculees')
ET.SubElement(pvc, 'variablesCalculees').text="false false false false false false false false false false false false false false false"
pvs = ET.SubElement(pc, 'parametresVariablesStockees')
ET.SubElement(pvs, 'variablesStockees').text="true false false false false true true true false false false false false false false false false false false true false false true false false false false false false false false false false false false false false false false false false false"
tree = ET.ElementTree(root)
xmlstr = ET.tostring(root, encoding='unicode', pretty_print=True)
tree.write(out_name)
print("fin du programme")
parser = ET.XMLParser(remove_blank_text=True)
tree = ET.parse(out_name, parser)
tree.write(out_name, pretty_print=True)
if __name__ == "__main__":
current_dir = os.getcwd()
os.chdir('/home/theophile.terraz/Codes/mage/src')
spec = imp.spec_from_file_location("wb", "./wrapper_bief.py")
wb = imp.module_from_spec(spec)
spec.loader.exec_module(wb)
os.chdir(current_dir)
print( 'Number of arguments:', len(sys.argv), 'arguments.')
print( 'Argument List:', str(sys.argv))
mage_to_mascaret(sys.argv[1])
+99
View File
@@ -0,0 +1,99 @@
#!/bin/env python3
import sys
import importlib.util as imp
import os
import numpy as np
def lire_opt(filename, res):
with open(filename, encoding = "ISO-8859-1", mode ="r") as f:
variables = ['"t"','"Bief"','"Section"','"Pk"']
lines = f.readlines()
for start,line in enumerate(lines):
print("line n "+str(start)+" : ",line)
if line == "[variables]\n": continue
if line == "[resultats]\n": break
val = line.split(';')
variables.append(val[1])
if val[1] == '"ZREF"': izref = start+3
if val[1] == '"Q"': iq = start+3
if val[1] == '"Z"': iz = start+3
print ("variables : "+str(variables))
#resultats
res.mage_version = 83
# first pass to count sect and biefs
prev_timestep = float(lines[start+1].split(';')[0])
for line in lines[start+1:]:
ls = line.split(';')
t = float(ls[0])
if prev_timestep != t : break
ibief = int(ls[1].strip('"'))
isect = int(ls[2].strip('"'))
pk = float(ls[3])
if ibief > res.ibmax:
res.ibmax += 1
res.is1.append(isect)
res.is2.append(isect)
if isect > res.ismax:
res.ismax += 1
res.xgeo.append(pk)
res.ygeo.append(0.0)
res.zfd.append(float(ls[izref]))
res.ybas.append(0.0)
if isect > res.is2[ibief-1]:
res.is2[ibief-1] = isect
if isect < res.is1[ibief-1]:
res.is1[ibief-1] = isect
print(" nb sections : "+str(res.ismax))
# first pass to get the data
prev_bief = 0
prev_sect = 0
z = np.zeros(res.ismax, dtype=np.float32)
q = np.zeros(res.ismax, dtype=np.float32)
isect = 0
count = np.array([1],dtype=np.int32)
bZ = bytearray('Z'.encode())
bQ = bytearray('Q'.encode())
for line in lines[start+1:]:
ls = line.split(';')
t = float(ls[0])
ibief = int(ls[1].strip('"'))
isect = int(ls[2].strip('"'))
if prev_timestep != t :
res.values['Z'].append(prev_timestep, z)
res.values['Q'].append(prev_timestep, q)
count[0] = 4 + 8 + 1 + 8*len(z)
res.raw_data.append(count)
res.raw_data.append(np.array([len(z)],dtype=np.int32))
res.raw_data.append(np.array([prev_timestep],dtype=np.float64))
res.raw_data.append(np.array(bZ,dtype=np.byte))
res.raw_data.append(z)
res.raw_data.append(count)
res.raw_data.append(count)
res.raw_data.append(np.array([len(q)],dtype=np.int32))
res.raw_data.append(np.array([prev_timestep],dtype=np.float64))
res.raw_data.append(np.array(bQ,dtype=np.byte))
res.raw_data.append(q)
res.raw_data.append(count)
prev_timestep = t
z[isect-1] = float(ls[iz])
q[isect-1] = float(ls[iq])
if __name__ == "__main__":
current_dir = os.getcwd()
os.chdir('/home/theophile.terraz/Codes/mage/src')
spec = imp.spec_from_file_location("me", "./mage_extraire.py")
me = imp.module_from_spec(spec)
spec.loader.exec_module(me)
os.chdir(current_dir)
print( 'Number of arguments:', len(sys.argv), 'arguments.')
print( 'Argument List:', str(sys.argv))
res = me.data()
lire_opt(sys.argv[1], res)
res.write_bin_8(sys.argv[2][0:-4])
+6 -243
View File
@@ -59,17 +59,6 @@ class AbstractSolver(object):
self._name = name
self._description = ""
self._path_input = ""
self._path_solver = ""
self._path_output = ""
self._cmd_input = ""
self._cmd_solver = ""
self._cmd_output = ""
self._process = None
self._output = None
# Last study running
self._study = None
@@ -92,7 +81,6 @@ class AbstractSolver(object):
("all_init_time", "000:00:00:00"),
("all_final_time", "999:99:00:00"),
("all_timestep", "300.0"),
("all_command_line_arguments", ""),
]
return lst
@@ -141,18 +129,6 @@ class AbstractSolver(object):
def description(self, description):
self._description = description
def set_input(self, path, cmd):
self._path_input = path
self._cmd_input = cmd
def set_solver(self, path, cmd):
self._path_solver = path
self._cmd_solver = cmd
def set_output(self, path, cmd):
self._path_output = path
self._cmd_output = cmd
##########
# Export #
##########
@@ -160,41 +136,6 @@ class AbstractSolver(object):
def export(self, study, repertory, qlog=None):
raise NotImplementedMethodeError(self, self.export)
def cmd_args(self, study):
"""Return solver command line arguments list
Returns:
Command line arguments list
"""
params = study.river.get_params(self.type)
args = params.get_by_key("all_command_line_arguments")
return args.split(" ")
def input_param(self):
"""Return input command line parameter(s)
Returns:
Returns input parameter(s) string
"""
raise NotImplementedMethodeError(self, self.input_param)
def output_param(self):
"""Return output command line parameter(s)
Returns:
Returns output parameter(s) string
"""
raise NotImplementedMethodeError(self, self.output_param)
def log_file(self):
"""Return log file name
Returns:
Returns log file name as string
"""
raise NotImplementedMethodeError(self, self.log_file)
###########
# RESULTS #
###########
@@ -208,195 +149,17 @@ class AbstractSolver(object):
# Run #
#######
def _install_dir(self):
return os.path.abspath(
os.path.join(
os.path.dirname(__file__),
"..", ".."
)
)
def _format_command(self, study, cmd, path=""):
"""Format command line
Args:
cmd: The command line
path: Optional path string (replace @path in cmd)
Returns:
The executable and list of arguments
"""
# HACK: Works in most case... Trust me i'm an engineer
cmd = cmd.replace("@install_dir", self._install_dir())
cmd = cmd.replace("@path", path.replace(" ", "%20"))
cmd = cmd.replace("@input", self.input_param())
cmd = cmd.replace("@output", self.output_param())
cmd = cmd.replace("@dir", self._process.workingDirectory())
cmd = cmd.replace("@args", " ".join(self.cmd_args(study)))
logger.debug(f"! {cmd}")
if cmd[0] == "\"":
# Command line executable path is between " char
cmd = cmd.split("\"")
exe = cmd[1].replace("%20", " ")
args = list(
filter(
lambda s: s != "",
"\"".join(cmd[2:]).split(" ")[1:]
)
)
else:
# We suppose the command line executable path as no space char
cmd = cmd.replace("\\ ", "&_&").split(" ")
exe = cmd[0].replace("&_&", " ")
args = list(
filter(
lambda s: s != "",
map(lambda s: s.replace("&_&", " "), cmd[1:])
)
)
logger.info(f"! {exe} {args}")
return exe, args
def run_input_data_fomater(self, study):
if self._cmd_input == "":
self._run_next(study)
return True
cmd = self._cmd_input
exe, args = self._format_command(study, cmd, self._path_input)
if not os.path.exists(exe):
error = f"[ERROR] Path {exe} do not exists"
logger.info(error)
return error
self._process.start(
exe, args,
)
return True
def run_solver(self, study):
if self._cmd_solver == "":
self._run_next(study)
return True
cmd = self._cmd_solver
exe, args = self._format_command(study, cmd, self._path_solver)
if not os.path.exists(exe):
error = f"[ERROR] Path {exe} do not exists"
logger.info(error)
return error
self._process.start(
exe, args,
)
self._process.waitForStarted()
self._status = STATUS.RUNNING
return True
def run_output_data_fomater(self, study):
if self._cmd_output == "":
self._run_next(study)
return True
cmd = self._cmd_output
exe, args = self._format_command(study, cmd, self._path_output)
if not os.path.exists(exe):
error = f"[ERROR] Path {exe} do not exists"
logger.info(error)
return error
self._process.start(
exe, args,
)
return True
def _data_ready(self):
s = self._process.readAll().data().decode()
if self._output is not None:
for x in s.split('\n'):
self._output.put(x)
def _run_next(self, study):
self._step += 1
if self._step < len(self._runs):
res = self._runs[self._step](study)
if res is not True:
self._output.put(res)
else:
self._status = STATUS.STOPED
def _finished(self, study, exit_code, exit_status):
if self._output is not None:
self._output.put(exit_code)
self._run_next(study)
def run(self, study, process=None, output_queue=None):
self._study = study
if process is not None:
self._process = process
if output_queue is not None:
self._output = output_queue
self._process.readyRead.connect(self._data_ready)
self._process.finished.connect(
lambda c, s: self._finished(study, c, s))
self._runs = [
self.run_input_data_fomater,
self.run_solver,
self.run_output_data_fomater,
]
self._step = 0
# Run first step
res = self._runs[0](study)
if res is not True:
self._output.put(res)
def run(self, study):
raise NotImplementedMethodeError(self, self.run)
def kill(self):
if self._process is None:
return True
self._process.kill()
self._status = STATUS.STOPED
return True
raise NotImplementedMethodeError(self, self.kill)
def start(self, study, process=None):
if _signal:
# Solver is PAUSED, so continue execution
if self._status == STATUS.PAUSED:
os.kill(self._process.pid(), SIGCONT)
self._status = STATUS.RUNNING
return True
self.run(study, process)
return True
raise NotImplementedMethodeError(self, self.start)
def pause(self):
if _signal:
if self._process is None:
return False
# Send SIGSTOP to PAUSED solver
os.kill(self._process.pid(), SIGSTOP)
self._status = STATUS.PAUSED
return True
return False
raise NotImplementedMethodeError(self, self.pause)
def stop(self):
if self._process is None:
return False
self._process.terminate()
self._status = STATUS.STOPED
return True
raise NotImplementedMethodeError(self, self.stop)
+309
View File
@@ -0,0 +1,309 @@
# CommandLine.py -- Pamhyr
# Copyright (C) 2023 INRAE
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# -*- coding: utf-8 -*-
import os
import logging
from tools import timer, parse_command_line
try:
# Installation allow Unix-like signal
from signal import SIGTERM, SIGSTOP, SIGCONT
_signal = True
except Exception:
_signal = False
from enum import Enum
from Model.Except import NotImplementedMethodeError
from Model.Results.Results import Results
from Model.Results.River.River import River, Reach, Profile
from Solver.ASolver import AbstractSolver, STATUS
logger = logging.getLogger()
class CommandLineSolver(AbstractSolver):
_type = ""
def __init__(self, name):
super(CommandLineSolver, self).__init__(name)
self._current_process = None
self._status = STATUS.NOT_LAUNCHED
self._path_input = ""
self._path_solver = ""
self._path_output = ""
self._cmd_input = ""
self._cmd_solver = ""
self._cmd_output = ""
self._process = None
self._output = None
# Last study running
self._study = None
@classmethod
def default_parameters(cls):
lst = super(CommandLineSolver, cls).default_parameters()
lst += [
("all_command_line_arguments", ""),
]
return lst
def set_input(self, path, cmd):
self._path_input = path
self._cmd_input = cmd
def set_solver(self, path, cmd):
self._path_solver = path
self._cmd_solver = cmd
def set_output(self, path, cmd):
self._path_output = path
self._cmd_output = cmd
##########
# Export #
##########
def cmd_args(self, study):
"""Return solver command line arguments list
Returns:
Command line arguments list
"""
params = study.river.get_params(self.type)
args = params.get_by_key("all_command_line_arguments")
return args.split(" ")
def input_param(self):
"""Return input command line parameter(s)
Returns:
Returns input parameter(s) string
"""
raise NotImplementedMethodeError(self, self.input_param)
def output_param(self):
"""Return output command line parameter(s)
Returns:
Returns output parameter(s) string
"""
raise NotImplementedMethodeError(self, self.output_param)
def log_file(self):
"""Return log file name
Returns:
Returns log file name as string
"""
raise NotImplementedMethodeError(self, self.log_file)
#######
# Run #
#######
def _install_dir(self):
return os.path.abspath(
os.path.join(
os.path.dirname(__file__),
"..", ".."
)
)
def _format_command(self, study, cmd, path=""):
"""Format command line
Args:
cmd: The command line
path: Optional path string (replace @path in cmd)
Returns:
The executable and list of arguments
"""
cmd = cmd.replace("@install_dir", self._install_dir())
cmd = cmd.replace("@path", "\"" + path + "\"")
cmd = cmd.replace("@input", self.input_param())
cmd = cmd.replace("@output", self.output_param())
cmd = cmd.replace("@dir", self._process.workingDirectory())
cmd = cmd.replace("@args", " ".join(self.cmd_args(study)))
logger.debug(f"! {cmd}")
words = parse_command_line(cmd)
exe = words[0]
args = words[1:]
logger.info(f"! {exe} {args}")
return exe, args
def run_input_data_fomater(self, study):
if self._cmd_input == "":
self._run_next(study)
return True
cmd = self._cmd_input
exe, args = self._format_command(study, cmd, self._path_input)
if not os.path.exists(exe):
error = f"[ERROR] Path {exe} do not exists"
logger.info(error)
return error
self._process.start(
exe, args,
)
return True
def run_solver(self, study):
if self._cmd_solver == "":
self._run_next(study)
return True
cmd = self._cmd_solver
exe, args = self._format_command(study, cmd, self._path_solver)
if not os.path.exists(exe):
error = f"[ERROR] Path {exe} do not exists"
logger.info(error)
return error
self._process.start(
exe, args,
)
self._process.waitForStarted()
self._status = STATUS.RUNNING
return True
def run_output_data_fomater(self, study):
if self._cmd_output == "":
self._run_next(study)
return True
cmd = self._cmd_output
exe, args = self._format_command(study, cmd, self._path_output)
if not os.path.exists(exe):
error = f"[ERROR] Path {exe} do not exists"
logger.info(error)
return error
self._process.start(
exe, args,
)
return True
def _data_ready(self):
# Read process output and put lines in queue
s = self._process.readAll().data().decode()
if self._output is not None:
for x in s.split('\n'):
self._output.put(x)
def _run_next(self, study):
self._step += 1
if self._step < len(self._runs):
res = self._runs[self._step](study)
if res is not True:
self._output.put(res)
else:
self._status = STATUS.STOPED
def _finished(self, study, exit_code, exit_status):
if self._output is not None:
self._output.put(exit_code)
self._run_next(study)
def run(self, study, process=None, output_queue=None):
self._study = study
# Replace old values if needed
if process is not None:
self._process = process
if output_queue is not None:
self._output = output_queue
# Connect / reconnect signal
self._process.readyRead.connect(self._data_ready)
self._process.finished.connect(
lambda c, s: self._finished(study, c, s))
# Prepare running step
self._runs = [
self.run_input_data_fomater,
self.run_solver,
self.run_output_data_fomater,
]
self._step = 0
# Run first step
res = self._runs[0](study)
if res is not True:
self._output.put(res)
def kill(self):
if self._process is None:
return True
self._process.kill()
self._status = STATUS.STOPED
return True
def start(self, study, process=None):
if _signal:
# Solver is PAUSED, so continue execution
if self._status == STATUS.PAUSED:
os.kill(self._process.pid(), SIGCONT)
self._status = STATUS.RUNNING
return True
self.run(study, process)
return True
def pause(self):
if _signal:
if self._process is None:
return False
# Send SIGSTOP to PAUSED solver
os.kill(self._process.pid(), SIGSTOP)
self._status = STATUS.PAUSED
return True
return False
def stop(self):
if self._process is None:
return False
self._process.terminate()
self._status = STATUS.STOPED
return True
+2 -4
View File
@@ -16,12 +16,10 @@
# -*- coding: utf-8 -*-
from Solver.ASolver import (
AbstractSolver, STATUS
)
from Solver.CommandLine import CommandLineSolver
class GenericSolver(AbstractSolver):
class GenericSolver(CommandLineSolver):
_type = "generic"
def __init__(self, name):
+112 -59
View File
@@ -22,7 +22,7 @@ import numpy as np
from tools import timer
from Solver.ASolver import AbstractSolver
from Solver.CommandLine import CommandLineSolver
from Checker.Mage import MageNetworkGraphChecker
from Model.Results.Results import Results
@@ -41,7 +41,7 @@ def mage_file_open(filepath, mode):
return f
class Mage(AbstractSolver):
class Mage(CommandLineSolver):
_type = "mage"
def __init__(self, name):
@@ -125,7 +125,6 @@ class Mage(AbstractSolver):
qlog.put("Export ST file")
os.makedirs(os.path.join(repertory, "net"), exist_ok=True)
gra_file = f"{name}.GRA"
# Write header
edges = study.river.edges()
@@ -149,72 +148,83 @@ class Mage(AbstractSolver):
cnt_num = 1
for profile in edge.reach.profiles:
num = f"{cnt_num:>6}"
c1 = f"{profile.code1:>6}"
c2 = f"{profile.code2:>6}"
t = f"{len(profile.points):>6}"
kp = f"{profile.kp:>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(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
f.write(f"{num}{c1}{c2}{t} {kp} {name} {sediment}\n")
self._export_ST_profile_header(
f, files, profile, cnt_num
)
cnt_num += 1
# Points
for point in profile.points:
x = f"{point.x:<12f}"[0:12]
y = f"{point.y:<12f}"[0:12]
z = f"{point.z:<12f}"[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
f.write(f"{x} {y} {z} {n} {sediment}\n")
self._export_ST_point_line(
f, files, point
)
# Profile last line
f.write(f" 999.9990 999.9990 999.9990\n")
return files
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}"
kp = f"{profile.kp:>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} {kp} {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")
@timer
def _export_BC(self, t, bounds, repertory, qlog, name="0"):
files = []
@@ -258,6 +268,9 @@ class Mage(AbstractSolver):
for tab in ["liquid", "solid", "suspenssion"]:
for bound in lst.get_tab(tab):
if bound.node is None:
continue
if bound.bctype == "ZD":
AVA.append(bound)
elif bound.bctype == "TD" or bound.bctype == "PC":
@@ -399,6 +412,36 @@ class Mage(AbstractSolver):
files.append(f"{name}.INI")
return files
@timer
def _export_CAS(self, study, repertory, qlog, name="0"):
files = []
reservoirs = study.river.reservoir.lst
if len(reservoirs) == 0:
return files
if qlog is not None:
qlog.put("Export CAS file")
with mage_file_open(os.path.join(repertory, f"{name}.CAS"), "w+") as f:
files.append(f"{name}.CAS")
for reservoir in reservoirs:
reservoir.sort()
node = reservoir.node
name = f"{node.id:3}".replace(" ", "x")
f.write(f"* {node.name} ({name}) Reservoir\n")
f.write(f"${name}\n")
f.write(f"*{'Elev(m)':>9}|{'Area(ha)':>10}\n")
for d in reservoir.data:
v0 = d[0]
v1 = d[1]
f.write(f"{v0:>10.3f}{v1:>10.3f}\n")
return files
@timer
def _export_REP(self, study, repertory, files, qlog, name="0"):
if qlog is not None:
@@ -429,6 +472,10 @@ class Mage(AbstractSolver):
self._study = study
name = study.name.replace(" ", "_")
# Define GRA file name
self._gra_file = f"{name}.GRA"
self._bin_file = f"{name}.BIN"
self._export_ST(study, repertory, qlog, name=name)
return True
@@ -622,6 +669,11 @@ class Mage8(Mage):
self._study = study
name = study.name.replace(" ", "_")
# Define GRA file name
self._gra_file = f"{name}.GRA"
self._bin_file = f"{name}.BIN"
# Generate files
files = []
files = self._export_ST(study, repertory, qlog, name=name)
@@ -631,6 +683,7 @@ class Mage8(Mage):
self._export_bound_cond(study, repertory, qlog, name=name)
files = files + self._export_RUG(study, repertory, qlog, name=name)
files = files + self._export_INI(study, repertory, qlog, name=name)
files = files + self._export_CAS(study, repertory, qlog, name=name)
self._export_REP(study, repertory, files, qlog, name=name)
return True
-1
View File
@@ -44,7 +44,6 @@ from Model.BoundaryCondition.BoundaryConditionTypes import (
from View.BoundaryCondition.Edit.UndoCommand import (
SetDataCommand, AddCommand, DelCommand,
SortCommand, MoveCommand, PasteCommand,
DuplicateCommand,
)
_translate = QCoreApplication.translate
+3 -18
View File
@@ -174,25 +174,10 @@ class PasteCommand(QUndoCommand):
self._bcs.reverse()
def undo(self):
self._data.delete(self._bcs)
self._data.delete_i(
range(self._row, self._row + len(self._bcs))
)
def redo(self):
for bc in self._bcs:
self._data.insert(self._row, bc)
class DuplicateCommand(QUndoCommand):
def __init__(self, data, rows, bc):
QUndoCommand.__init__(self)
self._data = data
self._rows = rows
self._bc = deepcopy(bc)
self._bc.reverse()
def undo(self):
self._data.delete(self._bc)
def redo(self):
for bc in self._bcs:
self._data.insert(self._rows[0], bc)
+3 -1
View File
@@ -160,7 +160,7 @@ class EditBoundaryConditionWindow(PamhyrWindow):
table_headers=headers,
editable_headers=self._data.header,
delegates={
"time": self._delegate_time,
#"time": self._delegate_time,
},
data=self._data,
undo=self._undo_stack,
@@ -289,6 +289,8 @@ class EditBoundaryConditionWindow(PamhyrWindow):
def _paste(self):
header, data = self.parseClipboardTable()
logger.debug(f"paste: h:{header}, d:{data}")
if len(data) == 0:
return
+5 -4
View File
@@ -44,7 +44,7 @@ from View.Tools.PamhyrTable import PamhyrTableModel
from View.BoundaryCondition.UndoCommand import (
SetNameCommand, SetNodeCommand, SetTypeCommand,
AddCommand, DelCommand, SortCommand,
MoveCommand, PasteCommand, DuplicateCommand,
MoveCommand, PasteCommand,
)
from View.BoundaryCondition.translate import BC_types
@@ -115,13 +115,14 @@ class ComboBoxDelegate(QItemDelegate):
class TableModel(PamhyrTableModel):
def __init__(self, **kwargs):
super(TableModel, self).__init__(**kwargs)
def __init__(self, trad = None, **kwargs):
self._trad = trad
self._long_types = {}
if self._trad is not None:
self._long_types = self._trad.get_dict("long_types")
super(TableModel, self).__init__(trad = trad, **kwargs)
def _setup_lst(self):
self._lst = self._data.boundary_condition
self._tab = self._opt_data
+11 -19
View File
@@ -53,12 +53,19 @@ class SetNodeCommand(QUndoCommand):
self._index = index
self._old = self._bcs.get(self._tab, self._index).node
self._new = node
self._prev_assoc_to_node = self._bcs.get_assoc_to_node(tab, node)
def _previous_assoc_node(self, node):
if self._prev_assoc_to_node is not None:
self._prev_assoc_to_node.node = node
def undo(self):
self._bcs.get(self._tab, self._index).node = self._old
self._previous_assoc_node(self._new)
def redo(self):
self._bcs.get(self._tab, self._index).node = self._new
self._previous_assoc_node(None)
class SetTypeCommand(QUndoCommand):
@@ -187,26 +194,11 @@ class PasteCommand(QUndoCommand):
self._bc.reverse()
def undo(self):
self._bcs.delete(self._tab, self._bc)
self._bcs.delete_i(
self._tab,
range(self._row, self._row + len(self._bc))
)
def redo(self):
for bc in self._bc:
self._bcs.insert(self._tab, self._row, bc)
class DuplicateCommand(QUndoCommand):
def __init__(self, bcs, tab, rows, bc):
QUndoCommand.__init__(self)
self._bcs = bcs
self._tab = tab
self._rows = rows
self._bc = deepcopy(bc)
self._bc.reverse()
def undo(self):
self._bcs.delete(self._tab, self._bc)
def redo(self):
for bc in self._bcs:
self._bcs.insert(self._tab, self._rows[0], bc)
+1 -1
View File
@@ -47,7 +47,7 @@ from Model.BoundaryCondition.BoundaryConditionTypes import (
from View.BoundaryCondition.UndoCommand import (
SetNameCommand, SetNodeCommand, SetTypeCommand,
AddCommand, DelCommand, SortCommand,
MoveCommand, PasteCommand, DuplicateCommand,
MoveCommand, PasteCommand,
)
from View.BoundaryCondition.Table import (
+3 -3
View File
@@ -45,9 +45,9 @@ class BCTranslate(PamhyrTranslate):
self._sub_dict["long_types"] = {
"ND": _translate("BoundaryCondition", "Not defined"),
"PC": _translate("BoundaryCondition", "Ponctual contribution"),
"TZ": _translate("BoundaryCondition", "TimeZ"),
"TD": _translate("BoundaryCondition", "TimeDischarge"),
"ZD": _translate("BoundaryCondition", "ZDischarge"),
"TZ": _translate("BoundaryCondition", "Z(t)"),
"TD": _translate("BoundaryCondition", "Q(t)"),
"ZD": _translate("BoundaryCondition", "Q(Z)"),
"SL": _translate("BoundaryCondition", "Solid"),
}
+13 -4
View File
@@ -16,6 +16,8 @@
# -*- coding: utf-8 -*-
import logging
from tools import timer, flatten
from View.Tools.PamhyrPlot import PamhyrPlot
@@ -23,13 +25,20 @@ from PyQt5.QtCore import (
QCoreApplication
)
logger = logging.getLogger()
_translate = QCoreApplication.translate
class PlotStricklers(PamhyrPlot):
def draw_frictions(self, frictions, color="r"):
lst = frictions
lst.sort(key=lambda s: s.begin_kp)
lst = sorted(
filter(
lambda f: f.is_full_defined(),
frictions
),
key=lambda s: s.begin_kp
)
coef = flatten(
map(
@@ -68,11 +77,11 @@ class PlotStricklers(PamhyrPlot):
self.canvas.axes.set_ylabel(
_translate("MainWindow_reach", "Stricklers"),
color='green', fontsize=11
color='black', fontsize=11
)
self.canvas.axes.set_xlabel(
_translate("MainWindow_reach", "Kp (m)"),
color='green', fontsize=11
color='black', fontsize=11
)
kp = self.data.reach.get_kp()
+1 -1
View File
@@ -28,7 +28,7 @@ class FrictionsTranslate(PamhyrTranslate):
super(FrictionsTranslate, self).__init__()
self._sub_dict["table_headers"] = {
"name": _translate("Frictions", "Name"),
# "name": _translate("Frictions", "Name"),
# "edge": _translate("Frictions", "Reach"),
"begin_kp": _translate("Frictions", "Begin kp (m)"),
"end_kp": _translate("Frictions", "End kp (m)"),
+6 -6
View File
@@ -72,11 +72,11 @@ class PlotAC(PamhyrPlot):
self.canvas.axes.set_xlabel(
_translate("MainWindow_reach", "Transverse abscissa (m)"),
color='green', fontsize=10
color='black', fontsize=10
)
self.canvas.axes.set_ylabel(
_translate("MainWindow_reach", "Height (m)"),
color='green', fontsize=10
color='black', fontsize=10
)
self.canvas.figure.tight_layout()
@@ -175,12 +175,12 @@ class PlotAC(PamhyrPlot):
self.canvas.axes.cla()
self.canvas.axes.grid(color='grey', linestyle='--', linewidth=0.5)
self.canvas.axes.set_xlabel(
_translate("MainWindow_reach", "Abscisse en travers (m)"),
color='green', fontsize=10
_translate("MainWindow_reach", "Transverse abscissa (m)"),
color='black', fontsize=10
)
self.canvas.axes.set_ylabel(
_translate("MainWindow_reach", "Cote (m)"),
color='green', fontsize=10
_translate("MainWindow_reach", "Height (m)"),
color='black', fontsize=10
)
self.canvas.figure.tight_layout()
+2 -2
View File
@@ -64,11 +64,11 @@ class PlotKPZ(PamhyrPlot):
self.canvas.axes.set_xlabel(
_translate("MainWindow_reach", "Kp (m)"),
color='green', fontsize=10
color='black', fontsize=10
)
self.canvas.axes.set_ylabel(
_translate("MainWindow_reach", "Height (m)"),
color='green', fontsize=10
color='black', fontsize=10
)
kp = self.data.get_kp()
+2 -2
View File
@@ -65,11 +65,11 @@ class PlotXY(PamhyrPlot):
# Axes
self.canvas.axes.set_xlabel(
_translate("Geometry", "X (m)"),
color='green', fontsize=10
color='black', fontsize=10
)
self.canvas.axes.set_ylabel(
_translate("Geometry", "Y (m)"),
color='green', fontsize=10
color='black', fontsize=10
)
self.canvas.axes.axis("equal")
+2 -2
View File
@@ -77,11 +77,11 @@ class Plot(PamhyrPlot):
)
self.canvas.axes.set_xlabel(
_translate("MainWindowProfile",
"Abscisse en travers (m)"),
"Transverse abscissa (m)"),
color='black', fontsize=10
)
self.canvas.axes.set_ylabel(
_translate("MainWindowProfile", "Cote (m)"),
_translate("MainWindowProfile", "Height (m)"),
color='black', fontsize=10
)
+2 -2
View File
@@ -37,11 +37,11 @@ class PlotDKP(PamhyrPlot):
self.canvas.axes.set_ylabel(
self._trad["elevation"],
color='green', fontsize=11
color='black', fontsize=11
)
self.canvas.axes.set_xlabel(
self._trad["kp"],
color='green', fontsize=11
color='black', fontsize=11
)
kp = self.data.reach.reach.get_kp()
+2 -2
View File
@@ -31,11 +31,11 @@ class PlotDischarge(PamhyrPlot):
self.canvas.axes.set_ylabel(
self._trad["discharge"],
color='green', fontsize=11
color='black', fontsize=11
)
self.canvas.axes.set_xlabel(
self._trad["kp"],
color='green', fontsize=11
color='black', fontsize=11
)
kp = self.data.reach.reach.get_kp()
+1 -1
View File
@@ -101,7 +101,7 @@ class EditLateralContributionWindow(PamhyrWindow):
table_headers=headers,
editable_headers=self._data.header,
delegates={
"time": self._delegate_time,
#"time": self._delegate_time,
},
data=self._data,
undo=self._undo_stack,
+17 -4
View File
@@ -47,6 +47,7 @@ from View.About.Window import AboutWindow
from View.Network.Window import NetworkWindow
from View.Geometry.Window import GeometryWindow
from View.BoundaryCondition.Window import BoundaryConditionWindow
from View.Reservoir.Window import ReservoirWindow
from View.LateralContribution.Window import LateralContributionWindow
from View.InitialConditions.Window import InitialConditionsWindow
from View.Stricklers.Window import StricklersWindow
@@ -101,7 +102,7 @@ define_model_action = [
"action_menu_boundary_conditions", "action_menu_initial_conditions",
"action_menu_edit_friction", "action_menu_edit_lateral_contribution",
"action_menu_run_solver", "action_menu_sediment_layers",
"action_menu_edit_reach_sediment_layers"
"action_menu_edit_reach_sediment_layers", "action_menu_edit_reservoirs"
]
action = (
@@ -139,8 +140,9 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
self.trans = QTranslator(self)
# self.ui.retranslateUi()
if self.conf.last_study != "" and not self.conf.close_correctly:
self.dialog_reopen_study()
if not self.conf.close_correctly:
if self.conf.last_study != "":
self.dialog_reopen_study()
if _doc == "external":
logger.info("doc: Internal documentation is not available...")
@@ -190,6 +192,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
"action_menu_edit_network": self.open_network,
"action_menu_edit_geometry": self.open_geometry,
"action_menu_boundary_conditions": self.open_boundary_cond,
"action_menu_edit_reservoirs": self.open_reservoir,
"action_menu_initial_conditions": self.open_initial_conditions,
"action_menu_edit_friction": self.open_frictions,
"action_menu_edit_lateral_contribution": self.open_lateral_contrib,
@@ -277,7 +280,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
"""
self.update_enable_action()
# Maximise window
self.showMaximized()
#self.showMaximized()
def set_debug_lvl(self, debug=True):
if debug:
@@ -615,6 +618,16 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
bound = BoundaryConditionWindow(study=self._study, parent=self)
bound.show()
def open_reservoir(self):
if self.sub_window_exists(
ReservoirWindow,
data=[self._study, None]
):
return
reservoir = ReservoirWindow(study=self._study, parent=self)
reservoir.show()
def open_lateral_contrib(self):
if self.sub_window_exists(
LateralContributionWindow,
+3 -1
View File
@@ -37,6 +37,8 @@ from PyQt5.QtWidgets import (
QApplication,
)
logger = logging.getLogger()
class ComboBoxDelegate(QItemDelegate):
def __init__(self, graph=None, parent=None):
@@ -144,7 +146,7 @@ class EdgeTableModel(PamhyrTableModel):
try:
if (self._headers[index.column()] == "node1" or
self._headers[index.column()] == "node2"):
node = self.graph.node(value)
node = self._data.node(value)
self._undo.push(
SetNodeCommand(
self._data,
+101
View File
@@ -0,0 +1,101 @@
# Plot.py -- Pamhyr
# Copyright (C) 2023 INRAE
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# -*- coding: utf-8 -*-
import logging
from datetime import datetime
from tools import timer, trace
from View.Tools.PamhyrPlot import PamhyrPlot
from PyQt5.QtCore import (
QCoreApplication
)
from View.BoundaryCondition.Edit.translate import BCETranslate
_translate = QCoreApplication.translate
logger = logging.getLogger()
class Plot(PamhyrPlot):
def __init__(self, mode="time", data=None,
trad=None, canvas=None, toolbar=None,
parent=None):
super(Plot, self).__init__(
canvas=canvas,
trad=trad,
data=data,
toolbar=toolbar,
parent=parent
)
self._table_headers = self._trad.get_dict("table_headers")
self._mode = mode
@timer
def draw(self):
self.canvas.axes.cla()
self.canvas.axes.grid(color='grey', linestyle='--', linewidth=0.5)
if len(self.data) == 0:
self._init = False
return
# Plot data
x = list(map(lambda v: v[0], self.data.data))
y = list(map(lambda v: v[1], self.data.data))
self._line, = self.canvas.axes.plot(
x, y,
color='r', lw=1.,
markersize=5, marker='+',
picker=30,
)
# Plot label
#header = self.data.header
self.canvas.axes.set_xlabel(
self._table_headers["z"], color='black', fontsize=10
)
self.canvas.axes.set_ylabel(
self._table_headers["Area"], color='black', fontsize=10
)
self.canvas.axes.autoscale_view(True, True, True)
self.canvas.figure.tight_layout()
self.canvas.figure.canvas.draw_idle()
# self.toolbar.update()
self._init = True
@timer
def update(self, ind=None):
if not self._init:
self.draw()
return
x = list(map(lambda v: v[0], self.data.data))
y = list(map(lambda v: v[1], self.data.data))
self._line.set_data(x, y)
self.canvas.axes.relim()
self.canvas.axes.autoscale()
self.canvas.figure.tight_layout()
self.canvas.figure.canvas.draw_idle()
+141
View File
@@ -0,0 +1,141 @@
# Table.py -- Pamhyr
# Copyright (C) 2023 INRAE
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# -*- coding: utf-8 -*-
import logging
import traceback
from datetime import date, time, datetime, timedelta
from tools import trace, timer
from View.Tools.PamhyrTable import PamhyrTableModel
from PyQt5.QtCore import (
Qt, QVariant, QAbstractTableModel,
QCoreApplication, QModelIndex, pyqtSlot,
QRect, QTime, QDateTime,
)
from PyQt5.QtWidgets import (
QTableView, QAbstractItemView, QSpinBox, QItemDelegate,
)
from View.Reservoir.Edit.UndoCommand import (
SetDataCommand, AddCommand, DelCommand,
SortCommand, PasteCommand,
)
_translate = QCoreApplication.translate
logger = logging.getLogger()
class TableModel(PamhyrTableModel):
def data(self, index, role):
if role == Qt.TextAlignmentRole:
return Qt.AlignHCenter | Qt.AlignVCenter
if role != Qt.ItemDataRole.DisplayRole:
return QVariant()
row = index.row()
column = index.column()
value = QVariant()
if 0 <= column < 2:
v = self._data.get_i(row)[column]
value = f"{v:.4f}"
return value
def setData(self, index, value, role=Qt.EditRole):
if not index.isValid() or role != Qt.EditRole:
return False
row = index.row()
column = index.column()
try:
self._undo.push(
SetDataCommand(
self._data, row, column, float(value)
)
)
except Exception as e:
logger.info(e)
logger.debug(traceback.format_exc())
self.dataChanged.emit(index, index)
return True
def add(self, row, parent=QModelIndex()):
self.beginInsertRows(parent, row, row - 1)
self._undo.push(
AddCommand(
self._data, row
)
)
self.endInsertRows()
self.layoutChanged.emit()
def delete(self, rows, parent=QModelIndex()):
self.beginRemoveRows(parent, rows[0], rows[-1])
self._undo.push(
DelCommand(
self._data, rows
)
)
self.endRemoveRows()
def sort(self, _reverse, parent=QModelIndex()):
self.layoutAboutToBeChanged.emit()
self._undo.push(
SortCommand(
self._data, _reverse
)
)
self.layoutAboutToBeChanged.emit()
self.layoutChanged.emit()
def paste(self, row, data):
if len(data) == 0:
return
self.layoutAboutToBeChanged.emit()
self._undo.push(
PasteCommand(
self._data, row,
list(
map(
lambda d: self._data.new_from_data(d),
data
)
)
)
)
self.layoutAboutToBeChanged.emit()
self.layoutChanged.emit()
+35
View File
@@ -0,0 +1,35 @@
# translate.py -- Pamhyr
# Copyright (C) 2023 INRAE
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# -*- coding: utf-8 -*-
from PyQt5.QtCore import QCoreApplication
from View.Tools.PamhyrTranslate import PamhyrTranslate
from View.Reservoir.Translate import ReservoirTranslate
_translate = QCoreApplication.translate
class EditReservoirTranslate(ReservoirTranslate):
def __init__(self):
super(EditReservoirTranslate, self).__init__()
self._sub_dict["table_headers"] = {
"z": _translate("Reservoir", "Elevation (m)"),
"Area": _translate("Reservoir", "Area (hectare)"),
}
+135
View File
@@ -0,0 +1,135 @@
# UndoCommand.py -- Pamhyr
# Copyright (C) 2023 INRAE
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# -*- coding: utf-8 -*-
import logging
from copy import deepcopy
from tools import trace, timer
from PyQt5.QtWidgets import (
QMessageBox, QUndoCommand, QUndoStack,
)
from Model.Reservoir.Reservoir import Reservoir
logger = logging.getLogger()
class SetDataCommand(QUndoCommand):
def __init__(self, data, index, column, new_value):
QUndoCommand.__init__(self)
self._data = data
self._index = index
self._column = column
self._old = self._data.get_i(self._index)[self._column]
self._new = new_value
def undo(self):
self._data._set_i_c_v(self._index, self._column, self._old)
def redo(self):
self._data._set_i_c_v(self._index, self._column, self._new)
class AddCommand(QUndoCommand):
def __init__(self, data, index):
QUndoCommand.__init__(self)
self._data = data
self._index = index
self._new = None
def undo(self):
self._data.delete_i([self._index])
def redo(self):
if self._new is None:
self._new = self._data.add(self._index)
else:
self._data.insert(self._index, self._new)
class DelCommand(QUndoCommand):
def __init__(self, data, rows):
QUndoCommand.__init__(self)
self._data = data
self._rows = rows
self._point = []
for row in rows:
self._point.append((row, self._data.get_i(row)))
self._point.sort()
def undo(self):
for row, el in self._point:
self._data.insert(row, el)
def redo(self):
self._data.delete_i(self._rows)
class SortCommand(QUndoCommand):
def __init__(self, data, _reverse):
QUndoCommand.__init__(self)
self._data = data
self._reverse = _reverse
self._old = self._data.data
self._indexes = None
def undo(self):
ll = self._data.data
self._data.sort(
key=lambda x: self._indexes[ll.index(x)]
)
def redo(self):
self._data.sort(
_reverse=self._reverse,
key=lambda x: x[0]
)
if self._indexes is None:
self._indexes = list(
map(
lambda p: self._old.index(p),
self._data.data
)
)
self._old = None
class PasteCommand(QUndoCommand):
def __init__(self, data, row, hs):
QUndoCommand.__init__(self)
self._data = data
self._row = row
self._h = hs
self._h.reverse()
def undo(self):
self._data.delete_i(
range(self._row, self._row + len(self._h))
)
def redo(self):
for h in self._h:
self._data.insert(self._row, h)
+217
View File
@@ -0,0 +1,217 @@
# Window.py -- Pamhyr
# Copyright (C) 2023 INRAE
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# -*- coding: utf-8 -*-
import logging
from tools import timer, trace
from View.Tools.PamhyrWindow import PamhyrWindow
from View.Tools.PamhyrWidget import PamhyrWidget
from PyQt5.QtGui import (
QKeySequence,
)
from PyQt5 import QtCore
from PyQt5.QtCore import (
Qt, QVariant, QAbstractTableModel, QCoreApplication,
pyqtSlot, pyqtSignal,
)
from PyQt5.QtWidgets import (
QDialogButtonBox, QPushButton, QLineEdit,
QFileDialog, QTableView, QAbstractItemView,
QUndoStack, QShortcut, QAction, QItemDelegate,
QHeaderView, QDoubleSpinBox, QVBoxLayout,
)
from View.Tools.Plot.PamhyrCanvas import MplCanvas
from View.Tools.Plot.PamhyrToolbar import PamhyrPlotToolbar
from View.Reservoir.Edit.Translate import EditReservoirTranslate
from View.Reservoir.Edit.Table import TableModel
from View.Reservoir.Edit.Plot import Plot
_translate = QCoreApplication.translate
logger = logging.getLogger()
class EditReservoirWindow(PamhyrWindow):
_pamhyr_ui = "Reservoir"
_pamhyr_name = "Edit Reservoir"
def __init__(self, data=None, study=None, config=None, parent=None):
self._data = data
trad = EditReservoirTranslate()
name = self._pamhyr_name
if self._data is not None:
node_name = (self._data.node.name if self._data.node is not None
else _translate("Reservoir", "Not associated"))
name = (
_translate("Edit Reservoir", self._pamhyr_name) +
f" - {study.name} " +
f" - {self._data.name} ({self._data.id}) " +
f"({node_name})"
)
super(EditReservoirWindow, self).__init__(
title=name,
study=study,
config=config,
trad=trad,
parent=parent
)
self._hash_data.append(data)
self.setup_table()
self.setup_plot()
self.setup_connections()
def setup_table(self):
headers = {}
table_headers = self._trad.get_dict("table_headers")
#for h in self._data.header:
#headers[h] = table_headers[h]
table = self.find(QTableView, "tableView")
self._table = TableModel(
table_view=table,
table_headers=table_headers,
editable_headers=table_headers,
#editable_headers=self._data.header,
delegates={
#"time": self._delegate_time,
},
data=self._data,
undo=self._undo_stack,
opt_data=self._study.time_system
)
table.setModel(self._table)
table.setSelectionBehavior(QAbstractItemView.SelectRows)
table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
table.setAlternatingRowColors(True)
def setup_plot(self):
self.canvas = MplCanvas(width=5, height=4, dpi=100)
self.canvas.setObjectName("canvas")
self.toolbar = PamhyrPlotToolbar(
self.canvas, self
)
self.verticalLayout.addWidget(self.toolbar)
self.verticalLayout.addWidget(self.canvas)
self.plot = Plot(
canvas=self.canvas,
data=self._data,
mode=self._study.time_system,
trad=self._trad,
toolbar=self.toolbar,
)
self.plot.draw()
def setup_connections(self):
self.find(QAction, "action_add").triggered.connect(self.add)
self.find(QAction, "action_delete").triggered.connect(self.delete)
self.find(QAction, "action_sort").triggered.connect(self.sort)
self._table.dataChanged.connect(self.update)
def update(self):
self.plot.update()
def index_selected_row(self):
table = self.find(QTableView, "tableView")
return table.selectionModel()\
.selectedRows()[0]\
.row()
def index_selected_rows(self):
table = self.find(QTableView, "tableView")
return list(
# Delete duplicate
set(
map(
lambda i: i.row(),
table.selectedIndexes()
)
)
)
def add(self):
rows = self.index_selected_rows()
if len(self._data) == 0 or len(rows) == 0:
self._table.add(0)
else:
self._table.add(rows[0])
self.plot.update()
def delete(self):
rows = self.index_selected_rows()
if len(rows) == 0:
return
self._table.delete(rows)
self.plot.update()
def sort(self):
self._table.sort(False)
self.plot.update()
def _copy(self):
rows = self.index_selected_rows()
table = []
#table.append(self._data.header)
table.append(self._trad.get_dict("table_headers"))
data = self._data.data
for row in rows:
table.append(list(data[row]))
self.copyTableIntoClipboard(table)
def _paste(self):
header, data = self.parseClipboardTable()
logger.debug(f"paste: h:{header}, d:{data}")
if len(data) == 0:
return
row = 0
rows = self.index_selected_rows()
if len(rows) != 0:
row = rows[0]
self._table.paste(row, data)
self.plot.update()
def _undo(self):
self._table.undo()
self.plot.update()
self.widget_update()
def _redo(self):
self._table.redo()
self.plot.update()
self.widget_update()
+170
View File
@@ -0,0 +1,170 @@
# Table.py -- Pamhyr
# Copyright (C) 2023 INRAE
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# -*- coding: utf-8 -*-
import logging
import traceback
from tools import trace, timer
from PyQt5.QtCore import (
Qt, QVariant, QAbstractTableModel,
QCoreApplication, QModelIndex, pyqtSlot,
QRect,
)
from PyQt5.QtWidgets import (
QDialogButtonBox, QPushButton, QLineEdit,
QFileDialog, QTableView, QAbstractItemView,
QUndoStack, QShortcut, QAction, QItemDelegate,
QComboBox,
)
from View.Tools.PamhyrTable import PamhyrTableModel
from View.Reservoir.UndoCommand import (
SetNameCommand, SetNodeCommand,
AddCommand, DelCommand, PasteCommand,
)
logger = logging.getLogger()
_translate = QCoreApplication.translate
class ComboBoxDelegate(QItemDelegate):
def __init__(self, data=None, trad=None, parent=None):
super(ComboBoxDelegate, self).__init__(parent)
self._data = data
self._trad = trad
def createEditor(self, parent, option, index):
self.editor = QComboBox(parent)
self.editor.addItems(
[_translate("Reservoir", "Not associated")] +
self._data.nodes_names()
)
self.editor.setCurrentText(index.data(Qt.DisplayRole))
return self.editor
def setEditorData(self, editor, index):
value = index.data(Qt.DisplayRole)
self.editor.currentTextChanged.connect(self.currentItemChanged)
def setModelData(self, editor, model, index):
text = str(editor.currentText())
model.setData(index, text)
editor.close()
editor.deleteLater()
def updateEditorGeometry(self, editor, option, index):
r = QRect(option.rect)
if self.editor.windowFlags() & Qt.Popup:
if editor.parent() is not None:
r.setTopLeft(self.editor.parent().mapToGlobal(r.topLeft()))
editor.setGeometry(r)
@pyqtSlot()
def currentItemChanged(self):
self.commitData.emit(self.sender())
class TableModel(PamhyrTableModel):
def _setup_lst(self):
self._lst = self._data.reservoir
def rowCount(self, parent):
return len(self._lst)
def data(self, index, role):
if role != Qt.ItemDataRole.DisplayRole:
return QVariant()
row = index.row()
column = index.column()
if self._headers[column] == "name":
return self._lst.get(row).name
elif self._headers[column] == "node":
n = self._lst.get(row).node
if n is None:
return _translate("Reservoir", "Not associated")
return n.name
return QVariant()
def setData(self, index, value, role=Qt.EditRole):
if not index.isValid() or role != Qt.EditRole:
return False
row = index.row()
column = index.column()
try:
if self._headers[column] == "name":
self._undo.push(
SetNameCommand(
self._lst, row, value
)
)
elif self._headers[column] == "node":
self._undo.push(
SetNodeCommand(
self._lst, row, self._data.node(value)
)
)
except Exception as e:
logger.info(e)
logger.debug(traceback.format_exc())
self.dataChanged.emit(index, index)
return True
def add(self, row, parent=QModelIndex()):
self.beginInsertRows(parent, row, row - 1)
self._undo.push(
AddCommand(
self._lst, row
)
)
self.endInsertRows()
self.layoutChanged.emit()
def delete(self, rows, parent=QModelIndex()):
self.beginRemoveRows(parent, rows[0], rows[-1])
self._undo.push(
DelCommand(
self._lst, rows
)
)
self.endRemoveRows()
self.layoutChanged.emit()
def undo(self):
self._undo.undo()
self.layoutChanged.emit()
def redo(self):
self._undo.redo()
self.layoutChanged.emit()
+33
View File
@@ -0,0 +1,33 @@
# translate.py -- Pamhyr
# Copyright (C) 2023 INRAE
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# -*- coding: utf-8 -*-
from PyQt5.QtCore import QCoreApplication
from View.Tools.PamhyrTranslate import PamhyrTranslate
_translate = QCoreApplication.translate
class ReservoirTranslate(PamhyrTranslate):
def __init__(self):
super(ReservoirTranslate, self).__init__()
self._sub_dict["table_headers"] = {
"name": _translate("Reservoir", "Name"),
"node": _translate("Reservoir", "Node")
}
+127
View File
@@ -0,0 +1,127 @@
# UndoCommand.py -- Pamhyr
# Copyright (C) 2023 INRAE
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# -*- coding: utf-8 -*-
from copy import deepcopy
from tools import trace, timer
from PyQt5.QtWidgets import (
QMessageBox, QUndoCommand, QUndoStack,
)
from Model.Reservoir.Reservoir import Reservoir
from Model.Reservoir.ReservoirList import ReservoirList
class SetNameCommand(QUndoCommand):
def __init__(self, reservoir_lst, index, new_value):
QUndoCommand.__init__(self)
self._reservoir_lst = reservoir_lst
self._index = index
self._old = self._reservoir_lst.get(self._index).name
self._new = str(new_value)
def undo(self):
self._reservoir_lst.get(self._index).name = self._old
def redo(self):
self._reservoir_lst.get(self._index).name = self._new
class SetNodeCommand(QUndoCommand):
def __init__(self, reservoir_lst, index, node):
QUndoCommand.__init__(self)
self._reservoir_lst = reservoir_lst
self._index = index
self._old = self._reservoir_lst.get(self._index).node
self._new = node
self._prev_assoc_to_node = self._reservoir_lst.get_assoc_to_node(node)
def _previous_assoc_node(self, node):
if self._prev_assoc_to_node is not None:
self._prev_assoc_to_node.node = node
def undo(self):
self._reservoir_lst.get(self._index).node = self._old
self._previous_assoc_node(self._new)
def redo(self):
self._reservoir_lst.get(self._index).node = self._new
self._previous_assoc_node(None)
class AddCommand(QUndoCommand):
def __init__(self, reservoir_lst, index):
QUndoCommand.__init__(self)
self._reservoir_lst = reservoir_lst
self._index = index
self._new = None
def undo(self):
self._reservoir_lst.delete_i([self._index])
def redo(self):
if self._new is None:
self._new = self._reservoir_lst.new(self._index)
else:
self._reservoir_lst.insert(self._index, self._new)
class DelCommand(QUndoCommand):
def __init__(self, reservoir_lst, rows):
QUndoCommand.__init__(self)
self._reservoir_lst = reservoir_lst
self._rows = rows
self._reservoir = []
for row in rows:
self._reservoir.append((row, self._reservoir_lst.get(row)))
self._reservoir.sort()
def undo(self):
for row, el in self._reservoir:
self._reservoir_lst.insert(row, el)
def redo(self):
self._reservoir_lst.delete_i(self._rows)
class PasteCommand(QUndoCommand):
def __init__(self, reservoir_lst, row, reservoir):
QUndoCommand.__init__(self)
self._reservoir_lst = reservoir_lst
self._row = row
self._reservoir = deepcopy(reservoir)
self._reservoir.reverse()
def undo(self):
self._reservoir_lst.delete_i(
self._tab,
range(self._row, self._row + len(self._reservoir))
)
def redo(self):
for r in self._reservoir:
self._reservoir_lst.insert(self._row, r)
+173
View File
@@ -0,0 +1,173 @@
# Window.py -- Pamhyr
# Copyright (C) 2023 INRAE
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# -*- coding: utf-8 -*-
import logging
from tools import trace, timer
from View.Tools.PamhyrWindow import PamhyrWindow
from PyQt5.QtGui import (
QKeySequence,
)
from PyQt5.QtCore import (
Qt, QVariant, QAbstractTableModel,
QCoreApplication, QModelIndex, pyqtSlot,
QRect,
)
from PyQt5.QtWidgets import (
QDialogButtonBox, QPushButton, QLineEdit,
QFileDialog, QTableView, QAbstractItemView,
QUndoStack, QShortcut, QAction, QItemDelegate,
QComboBox, QVBoxLayout, QHeaderView, QTabWidget,
)
from View.Reservoir.Table import (
TableModel, ComboBoxDelegate
)
from View.Network.GraphWidget import GraphWidget
from View.Reservoir.Translate import ReservoirTranslate
from View.Reservoir.Edit.Window import EditReservoirWindow
_translate = QCoreApplication.translate
logger = logging.getLogger()
class ReservoirWindow(PamhyrWindow):
_pamhyr_ui = "ReservoirList"
_pamhyr_name = "Reservoir"
def __init__(self, study=None, config=None, parent=None):
name = self._pamhyr_name + " - " + study.name
super(ReservoirWindow, self).__init__(
title=name,
study=study,
config=config,
trad=ReservoirTranslate(),
parent=parent
)
self._reservoir_lst = self._study.river.reservoir
self.setup_table()
self.setup_graph()
self.setup_connections()
def setup_table(self):
self._table = None
self._delegate_node = ComboBoxDelegate(
trad=self._trad,
data=self._study.river,
parent=self
)
table = self.find(QTableView, f"tableView")
self._table = TableModel(
table_view=table,
table_headers=self._trad.get_dict("table_headers"),
editable_headers=["name", "node"],
delegates={
"node": self._delegate_node,
},
trad=self._trad,
data=self._study.river,
undo=self._undo_stack,
)
def setup_graph(self):
self.graph_widget = GraphWidget(
self._study.river,
min_size=None, size=(200, 200),
only_display=True,
parent=self
)
self.graph_layout = self.find(QVBoxLayout, "verticalLayout")
self.graph_layout.addWidget(self.graph_widget)
def setup_connections(self):
self.find(QAction, "action_add").triggered.connect(self.add)
self.find(QAction, "action_delete").triggered.connect(self.delete)
self.find(QAction, "action_edit").triggered.connect(self.edit)
def index_selected_row(self):
table = self.find(QTableView, "tableView")
return table.selectionModel()\
.selectedRows()[0]\
.row()
def index_selected_rows(self):
table = self.find(QTableView, "tableView")
return list(
# Delete duplicate
set(
map(
lambda i: i.row(),
table.selectedIndexes()
)
)
)
def add(self):
rows = self.index_selected_rows()
if len(self._reservoir_lst) == 0 or len(rows) == 0:
self._table.add(0)
else:
self._table.add(rows[0])
def delete(self):
rows = self.index_selected_rows()
if len(rows) == 0:
return
self._table.delete(rows)
def _copy(self):
logger.info("TODO: copy")
def _paste(self):
logger.info("TODO: paste")
def _undo(self):
self._table.undo()
def _redo(self):
self._table.redo()
def edit(self):
rows = self.index_selected_rows()
for row in rows:
data = self._reservoir_lst.get(row)
if self.sub_window_exists(
EditReservoirWindow,
data=[self._study, None, data]
):
continue
win = EditReservoirWindow(
data=data,
study=self._study,
parent=self
)
win.show()
+3 -3
View File
@@ -292,12 +292,12 @@ class CustomPlot(PamhyrPlot):
self.canvas.axes.set_xlabel(
self._trad[self._x],
color='green', fontsize=10
color='black', fontsize=10
)
self.canvas.axes.set_ylabel(
self._trad[self._y_axes[0]],
color='green', fontsize=10
color='black', fontsize=10
)
for axes in self._y_axes[1:]:
@@ -307,7 +307,7 @@ class CustomPlot(PamhyrPlot):
ax_new = self.canvas.axes.twinx()
ax_new.set_ylabel(
self._trad[axes],
color='green', fontsize=10
color='black', fontsize=10
)
self._axes[axes] = ax_new
+2 -2
View File
@@ -61,11 +61,11 @@ class PlotAC(PamhyrPlot):
self.canvas.axes.set_xlabel(
_translate("MainWindow_reach", "X (m)"),
color='green', fontsize=11
color='black', fontsize=11
)
self.canvas.axes.set_ylabel(
_translate("MainWindow_reach", "Elevation (m)"),
color='green', fontsize=11
color='black', fontsize=11
)
reach = self.results.river.reach(self._current_reach_id)
+2 -2
View File
@@ -82,11 +82,11 @@ class PlotH(PamhyrPlot):
# Axes
self.canvas.axes.set_xlabel(
_translate("Results", "Time (s)"),
color='green', fontsize=10
color='black', fontsize=10
)
self.canvas.axes.set_ylabel(
_translate("Results", "Discharge (m³/s)"),
color='green', fontsize=10
color='black', fontsize=10
)
ts = list(self.results.get("timestamps"))
+2 -2
View File
@@ -63,11 +63,11 @@ class PlotKPC(PamhyrPlot):
self.canvas.axes.set_ylabel(
_translate("MainWindow_reach", "Elevation (m)"),
color='green', fontsize=11
color='black', fontsize=11
)
self.canvas.axes.set_xlabel(
_translate("MainWindow_reach", "KP (m)"),
color='green', fontsize=11
color='black', fontsize=11
)
kp = reach.geometry.get_kp()
+2 -2
View File
@@ -132,11 +132,11 @@ class PlotSedProfile(PamhyrPlot):
self.canvas.axes.set_xlabel(
_translate("MainWindow_reach", "X (m)"),
color='green', fontsize=10
color='black', fontsize=10
)
self.canvas.axes.set_ylabel(
_translate("MainWindow_reach", "Height (m)"),
color='green', fontsize=10
color='black', fontsize=10
)
x = profile.geometry.get_station()
+2 -2
View File
@@ -213,11 +213,11 @@ class PlotSedReach(PamhyrPlot):
self.canvas.axes.set_xlabel(
_translate("MainWindow_reach", "Kp (m)"),
color='green', fontsize=10
color='black', fontsize=10
)
self.canvas.axes.set_ylabel(
_translate("MainWindow_reach", "Height (m)"),
color='green', fontsize=10
color='black', fontsize=10
)
kp = reach.geometry.get_kp()
+2 -2
View File
@@ -83,11 +83,11 @@ class PlotXY(PamhyrPlot):
# Axes
self.canvas.axes.set_xlabel(
_translate("Results", "X (m)"),
color='green', fontsize=10
color='black', fontsize=10
)
self.canvas.axes.set_ylabel(
_translate("Results", "Y (m)"),
color='green', fontsize=10
color='black', fontsize=10
)
self.canvas.axes.axis("equal")
+12 -5
View File
@@ -156,6 +156,7 @@ class SolverLogWindow(PamhyrWindow):
def new_process(self, parent):
new = QProcess(parent)
new.setWorkingDirectory(self._workdir)
new.setProcessChannelMode(QProcess.MergedChannels)
return new
def setup_action(self):
@@ -215,12 +216,18 @@ class SolverLogWindow(PamhyrWindow):
if self._solver.log_file() != "":
self.find(QAction, "action_log_file").setEnabled(True)
# Get results
if self._results is None:
self._results = self._solver.results(
self._study, self._workdir, qlog=self._output)
self._parent.set_results(self._solver, self._results)
self._update_get_results()
self._update_logs_all()
def _update_get_results(self):
if self._results is None:
self._results = self._solver.results(
self._study, self._workdir, qlog=self._output
)
self._parent.set_results(self._solver, self._results)
def _update_logs_all(self):
while self._output.qsize() != 0:
s = self._output.get()
if type(s) is str and "[ERROR]" in s:
+1 -1
View File
@@ -35,7 +35,7 @@ class Plot(PamhyrPlot):
self.canvas.axes.axes.get_xaxis().set_visible(False)
self.canvas.axes.set_ylabel(
self._trad["height"],
color='green', fontsize=10
color='black', fontsize=10
)
if self.data is None:
+2 -2
View File
@@ -46,11 +46,11 @@ class Plot(PamhyrPlot):
self.canvas.axes.set_xlabel(
self._trad["kp"],
color='green', fontsize=10
color='black', fontsize=10
)
self.canvas.axes.set_ylabel(
self._trad["height"],
color='green', fontsize=10
color='black', fontsize=10
)
kp = self.data.get_kp()
@@ -46,11 +46,11 @@ class Plot(PamhyrPlot):
self.canvas.axes.set_xlabel(
_translate("MainWindow_reach", "X (m)"),
color='green', fontsize=10
color='black', fontsize=10
)
self.canvas.axes.set_ylabel(
_translate("MainWindow_reach", "Height (m)"),
color='green', fontsize=10
color='black', fontsize=10
)
x = self.data.get_station()
+8 -2
View File
@@ -68,14 +68,20 @@ class WindowToolKit(object):
header = []
values = []
delimiter = '\t'
if ';' in data:
delimiter = ';'
if ' ' in data:
delimiter = ' '
stream = StringIO(data)
rows = csv.reader(stream, delimiter='\t')
rows = csv.reader(stream, delimiter=delimiter)
for ind, row in enumerate(rows):
if has_header and ind == 0:
header = row.copy()
continue
values.append(row)
values.append(list(filter(lambda s: s != '', row)))
return header, values
+1 -1
View File
@@ -32,7 +32,7 @@ class MplCanvas(FigureCanvasQTAgg):
'(x = ' + format(x, '1.4f') + ', \t' +
' y = ' + format(y, '1.4f') + ')'
)
self.axes.grid(color='green', linestyle='--', linewidth=0.5)
self.axes.grid(color='black', linestyle='--', linewidth=0.5)
self.axes.yaxis.tick_left()
self.axes.xaxis.tick_bottom()
self.axes.spines[['top', 'right']].set_color('none')
+34 -45
View File
@@ -82,9 +82,7 @@
<string>&amp;File</string>
</property>
<addaction name="action_menu_new"/>
<addaction name="separator"/>
<addaction name="action_menu_open"/>
<addaction name="separator"/>
<addaction name="action_menu_close"/>
<addaction name="separator"/>
<addaction name="action_menu_edit"/>
@@ -92,9 +90,7 @@
<addaction name="action_menu_save_as"/>
<addaction name="separator"/>
<addaction name="action_menu_config"/>
<addaction name="separator"/>
<addaction name="action_menu_quit"/>
<addaction name="separator"/>
</widget>
<widget class="QMenu" name="menu_network">
<property name="locale">
@@ -113,7 +109,6 @@
<string>&amp;Geometry</string>
</property>
<addaction name="action_menu_edit_geometry"/>
<addaction name="separator"/>
</widget>
<widget class="QMenu" name="menu_run">
<property name="locale">
@@ -123,9 +118,7 @@
<string>&amp;Execute</string>
</property>
<addaction name="action_menu_numerical_parameter"/>
<addaction name="separator"/>
<addaction name="action_menu_run_solver"/>
<addaction name="separator"/>
</widget>
<widget class="QMenu" name="menu_Hydraulics">
<property name="locale">
@@ -135,11 +128,10 @@
<string>&amp;Hydraulics</string>
</property>
<addaction name="action_menu_boundary_conditions"/>
<addaction name="separator"/>
<addaction name="action_menu_initial_conditions"/>
<addaction name="separator"/>
<addaction name="action_menu_edit_friction"/>
<addaction name="action_menu_edit_lateral_contribution"/>
<addaction name="action_menu_edit_reservoirs"/>
</widget>
<widget class="QMenu" name="menu_results">
<property name="title">
@@ -209,6 +201,7 @@
</property>
<property name="font">
<font>
<family>Sans Serif</family>
<weight>50</weight>
<bold>false</bold>
<kerning>true</kerning>
@@ -247,37 +240,14 @@
<addaction name="action_toolBar_open"/>
<addaction name="action_toolBar_save"/>
<addaction name="action_toolBar_close"/>
<addaction name="action_toolBar_quit"/>
<addaction name="separator"/>
<addaction name="action_toolBar_run_solver"/>
<addaction name="separator"/>
<addaction name="action_toolBar_network"/>
<addaction name="action_toolBar_geometry"/>
</widget>
<widget class="QToolBar" name="toolBar_2">
<property name="font">
<font>
<family>Sans Serif</family>
<pointsize>9</pointsize>
<weight>50</weight>
<italic>false</italic>
<bold>false</bold>
</font>
</property>
<property name="windowTitle">
<string>toolBar_2</string>
</property>
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>true</bool>
</attribute>
<addaction name="action_toolBar_boundary_cond"/>
<addaction name="separator"/>
<addaction name="action_toolBar_lateral_contrib"/>
<addaction name="separator"/>
<addaction name="action_toolBar_frictions"/>
<addaction name="separator"/>
<addaction name="action_toolBar_initial_cond"/>
</widget>
<action name="action_menu_new">
@@ -376,6 +346,9 @@
<property name="text">
<string>Save</string>
</property>
<property name="toolTip">
<string>Save study</string>
</property>
<property name="shortcut">
<string>Ctrl+S</string>
</property>
@@ -388,6 +361,9 @@
<property name="text">
<string>Save as ...</string>
</property>
<property name="toolTip">
<string>Save study as ...</string>
</property>
<property name="shortcut">
<string>Ctrl+Shift+S</string>
</property>
@@ -410,6 +386,9 @@
<property name="text">
<string>Quit</string>
</property>
<property name="toolTip">
<string>Quit application</string>
</property>
<property name="shortcut">
<string>Ctrl+F4</string>
</property>
@@ -514,10 +493,7 @@
<string>Boundary conditions and one-time contributions</string>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
<font/>
</property>
</action>
<action name="action_menu_initial_conditions">
@@ -618,12 +594,11 @@
<property name="text">
<string>Visualize last results</string>
</property>
<property name="toolTip">
<string>Visualize the last results</string>
</property>
<property name="font">
<font>
<family>Ubuntu</family>
<weight>75</weight>
<bold>true</bold>
</font>
<font/>
</property>
</action>
<action name="action_plot_limnigram">
@@ -690,7 +665,7 @@
<string>Save current study</string>
</property>
<property name="toolTip">
<string>Enrégistrer étude en cours (Ctrl+S)</string>
<string>Save the study (Ctrl+S)</string>
</property>
<property name="shortcut">
<string/>
@@ -705,7 +680,7 @@
<string>Close current study</string>
</property>
<property name="toolTip">
<string>Fermer étude en cours (Ctrl+F)</string>
<string>Close the study (Ctrl+F)</string>
</property>
<property name="shortcut">
<string>Ctrl+F</string>
@@ -879,7 +854,7 @@
<string>Edit study</string>
</property>
<property name="toolTip">
<string>Edit study</string>
<string>Edit the study metadata</string>
</property>
</action>
<action name="action_english">
@@ -904,6 +879,9 @@
<property name="text">
<string>Sediment layers</string>
</property>
<property name="toolTip">
<string>Define sediment layers</string>
</property>
</action>
<action name="action_menu_edit_reach_sediment_layers">
<property name="text">
@@ -924,6 +902,9 @@
<property name="text">
<string>Mage</string>
</property>
<property name="toolTip">
<string>Open mage documentation</string>
</property>
</action>
<action name="action_menu_pamhyr_users_pdf">
<property name="text">
@@ -945,6 +926,14 @@
<string>Developers (html)</string>
</property>
</action>
<action name="action_menu_edit_reservoirs">
<property name="text">
<string>Reservoirs</string>
</property>
<property name="toolTip">
<string>Edit reservoirs</string>
</property>
</action>
</widget>
<resources/>
<connections>
+120
View File
@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QTableView" name="tableView">
<property name="minimumSize">
<size>
<width>300</width>
<height>0</height>
</size>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</widget>
<widget class="QWidget" name="verticalLayoutWidget">
<layout class="QVBoxLayout" name="verticalLayout"/>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>22</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<widget class="QToolBar" name="toolBar">
<property name="windowTitle">
<string>toolBar</string>
</property>
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
<addaction name="action_add"/>
<addaction name="action_delete"/>
<addaction name="action_sort"/>
</widget>
<action name="action_add">
<property name="icon">
<iconset>
<normaloff>ressources/gtk-add.png</normaloff>ressources/gtk-add.png</iconset>
</property>
<property name="text">
<string>Add</string>
</property>
<property name="toolTip">
<string>Add a new point</string>
</property>
</action>
<action name="action_delete">
<property name="icon">
<iconset>
<normaloff>ressources/gtk-remove.png</normaloff>ressources/gtk-remove.png</iconset>
</property>
<property name="text">
<string>Delete</string>
</property>
<property name="toolTip">
<string>Delete points</string>
</property>
</action>
<action name="action_edit">
<property name="icon">
<iconset>
<normaloff>ressources/edit.png</normaloff>ressources/edit.png</iconset>
</property>
<property name="text">
<string>Edit</string>
</property>
<property name="toolTip">
<string>Edit elevation/surface law</string>
</property>
</action>
<action name="action_sort">
<property name="icon">
<iconset>
<normaloff>ressources/gtk-sort-ascending.png</normaloff>ressources/gtk-sort-ascending.png</iconset>
</property>
<property name="text">
<string>Sort</string>
</property>
<property name="toolTip">
<string>Sort points by elevation</string>
</property>
</action>
</widget>
<resources/>
<connections/>
</ui>
Loaded 100 of 104 files, more files were not shown because too many files have changed in this diff. Show more