Cycles-utils 4.0.6__tar.gz → 4.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {cycles_utils-4.0.6 → cycles_utils-4.2.0}/Cycles_utils.egg-info/PKG-INFO +33 -9
- {cycles_utils-4.0.6 → cycles_utils-4.2.0}/Cycles_utils.egg-info/SOURCES.txt +6 -2
- cycles_utils-4.2.0/Cycles_utils.egg-info/requires.txt +44 -0
- {cycles_utils-4.0.6 → cycles_utils-4.2.0}/PKG-INFO +33 -9
- {cycles_utils-4.0.6 → cycles_utils-4.2.0}/README.md +2 -0
- cycles_utils-4.2.0/cycles/__init__.py +67 -0
- cycles_utils-4.2.0/cycles/_base_file.py +98 -0
- {cycles_utils-4.0.6 → cycles_utils-4.2.0}/cycles/cycles.py +5 -40
- cycles_utils-4.2.0/cycles/cycles_runner.py +370 -0
- cycles_utils-4.2.0/cycles/cycles_tools/__init__.py +72 -0
- cycles_utils-4.2.0/cycles/cycles_tools/_geo_file.py +25 -0
- {cycles_utils-4.0.6 → cycles_utils-4.2.0}/cycles/cycles_tools/control_file.py +18 -13
- cycles_utils-4.2.0/cycles/cycles_tools/converter.py +290 -0
- cycles_utils-4.2.0/cycles/cycles_tools/crop_file.py +159 -0
- {cycles_utils-4.0.6 → cycles_utils-4.2.0}/cycles/cycles_tools/nudge_file.py +25 -22
- {cycles_utils-4.0.6 → cycles_utils-4.2.0}/cycles/cycles_tools/operation_file.py +39 -40
- {cycles_utils-4.0.6 → cycles_utils-4.2.0}/cycles/cycles_tools/plot_tools.py +6 -6
- {cycles_utils-4.0.6 → cycles_utils-4.2.0}/cycles/cycles_tools/soil_file.py +18 -19
- {cycles_utils-4.0.6 → cycles_utils-4.2.0}/cycles/gadm/gadm.py +71 -5
- cycles_utils-4.2.0/cycles/rotation_builder.py +679 -0
- {cycles_utils-4.0.6 → cycles_utils-4.2.0}/cycles/soilgrids/__init__.py +0 -3
- {cycles_utils-4.0.6 → cycles_utils-4.2.0}/cycles/soilgrids/soilgrids.py +12 -10
- cycles_utils-4.2.0/cycles/ssurgo/__init__.py +5 -0
- {cycles_utils-4.0.6 → cycles_utils-4.2.0}/cycles/ssurgo/ssurgo.py +13 -9
- {cycles_utils-4.0.6 → cycles_utils-4.2.0}/cycles/weather/__init__.py +0 -1
- {cycles_utils-4.0.6 → cycles_utils-4.2.0}/cycles/weather/weather.py +9 -6
- cycles_utils-4.2.0/setup.py +35 -0
- cycles_utils-4.2.0/test/test_runner.py +45 -0
- cycles_utils-4.0.6/Cycles_utils.egg-info/requires.txt +0 -19
- cycles_utils-4.0.6/cycles/__init__.py +0 -19
- cycles_utils-4.0.6/cycles/cycles_runner.py +0 -287
- cycles_utils-4.0.6/cycles/cycles_tools/__init__.py +0 -38
- cycles_utils-4.0.6/cycles/cycles_tools/_base_file.py +0 -75
- cycles_utils-4.0.6/cycles/rotation_builder.py +0 -486
- cycles_utils-4.0.6/cycles/ssurgo/__init__.py +0 -8
- cycles_utils-4.0.6/setup.py +0 -25
- {cycles_utils-4.0.6 → cycles_utils-4.2.0}/Cycles_utils.egg-info/dependency_links.txt +0 -0
- {cycles_utils-4.0.6 → cycles_utils-4.2.0}/Cycles_utils.egg-info/top_level.txt +0 -0
- {cycles_utils-4.0.6 → cycles_utils-4.2.0}/LICENSE +0 -0
- {cycles_utils-4.0.6 → cycles_utils-4.2.0}/MANIFEST.in +0 -0
- {cycles_utils-4.0.6 → cycles_utils-4.2.0}/cycles/cycles_tools/output_file.py +0 -0
- {cycles_utils-4.0.6 → cycles_utils-4.2.0}/cycles/cycles_tools/reinit_file.py +0 -0
- {cycles_utils-4.0.6 → cycles_utils-4.2.0}/cycles/cycles_tools/weather_file.py +0 -0
- {cycles_utils-4.0.6 → cycles_utils-4.2.0}/cycles/data/GLDASp5_elevation_025d.nc4 +0 -0
- {cycles_utils-4.0.6 → cycles_utils-4.2.0}/cycles/data/GLDASp5_landmask_025d.nc4 +0 -0
- {cycles_utils-4.0.6 → cycles_utils-4.2.0}/cycles/data/NLDAS_elevation.nc4 +0 -0
- {cycles_utils-4.0.6 → cycles_utils-4.2.0}/cycles/data/NLDAS_masks-veg-soil.nc4 +0 -0
- {cycles_utils-4.0.6 → cycles_utils-4.2.0}/cycles/data/cb_2018_us_state_20m.cpg +0 -0
- {cycles_utils-4.0.6 → cycles_utils-4.2.0}/cycles/data/cb_2018_us_state_20m.dbf +0 -0
- {cycles_utils-4.0.6 → cycles_utils-4.2.0}/cycles/data/cb_2018_us_state_20m.prj +0 -0
- {cycles_utils-4.0.6 → cycles_utils-4.2.0}/cycles/data/cb_2018_us_state_20m.shp +0 -0
- {cycles_utils-4.0.6 → cycles_utils-4.2.0}/cycles/data/cb_2018_us_state_20m.shx +0 -0
- {cycles_utils-4.0.6 → cycles_utils-4.2.0}/cycles/data/fips_gid_conversion.csv +0 -0
- {cycles_utils-4.0.6 → cycles_utils-4.2.0}/cycles/data/gridMET_elevation_mask.nc +0 -0
- {cycles_utils-4.0.6 → cycles_utils-4.2.0}/cycles/data/metdata_elevationdata.nc +0 -0
- {cycles_utils-4.0.6 → cycles_utils-4.2.0}/cycles/data/us_states.csv +0 -0
- {cycles_utils-4.0.6 → cycles_utils-4.2.0}/cycles/gadm/__init__.py +0 -0
- {cycles_utils-4.0.6 → cycles_utils-4.2.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Cycles-utils
|
|
3
|
-
Version: 4.0
|
|
3
|
+
Version: 4.2.0
|
|
4
4
|
Summary: Python scripts to build Cycles input files and post-process Cycles output files
|
|
5
5
|
Home-page: https://github.com/PSUmodeling/Cycles-utils
|
|
6
6
|
Author: Yuning Shi
|
|
@@ -10,21 +10,43 @@ Requires-Python: >=3.10
|
|
|
10
10
|
Description-Content-Type: text/markdown
|
|
11
11
|
License-File: LICENSE
|
|
12
12
|
Requires-Dist: pandas>=1.2.4
|
|
13
|
-
Requires-Dist: geopandas>=0.9.0
|
|
14
13
|
Requires-Dist: numpy>=1.19.5
|
|
15
|
-
Requires-Dist: cartopy>=0.18.0
|
|
16
14
|
Requires-Dist: matplotlib>=3.4.2
|
|
15
|
+
Requires-Dist: tqdm>=4.60.0
|
|
16
|
+
Requires-Dist: IPython>=7.0.0
|
|
17
|
+
Provides-Extra: plot
|
|
18
|
+
Requires-Dist: cartopy>=0.18.0; extra == "plot"
|
|
19
|
+
Requires-Dist: geopandas>=0.9.0; extra == "plot"
|
|
20
|
+
Requires-Dist: fiona>=1.8.20; extra == "plot"
|
|
21
|
+
Requires-Dist: shapely>=1.7.1; extra == "plot"
|
|
22
|
+
Provides-Extra: weather
|
|
23
|
+
Requires-Dist: netCDF4>=1.5.7; extra == "weather"
|
|
24
|
+
Requires-Dist: scipy>=1.6.3; extra == "weather"
|
|
17
25
|
Provides-Extra: soilgrids
|
|
26
|
+
Requires-Dist: geopandas>=0.9.0; extra == "soilgrids"
|
|
18
27
|
Requires-Dist: rioxarray>=0.5.0; extra == "soilgrids"
|
|
19
28
|
Requires-Dist: owslib>=0.24.1; extra == "soilgrids"
|
|
20
29
|
Requires-Dist: rasterio>=1.2.3; extra == "soilgrids"
|
|
21
30
|
Requires-Dist: shapely>=1.7.1; extra == "soilgrids"
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
Requires-Dist:
|
|
25
|
-
|
|
26
|
-
Requires-Dist:
|
|
27
|
-
|
|
31
|
+
Requires-Dist: pyproj>=3.0.1; extra == "soilgrids"
|
|
32
|
+
Provides-Extra: ssurgo
|
|
33
|
+
Requires-Dist: geopandas>=0.9.0; extra == "ssurgo"
|
|
34
|
+
Requires-Dist: shapely>=1.7.1; extra == "ssurgo"
|
|
35
|
+
Requires-Dist: fiona>=1.8.20; extra == "ssurgo"
|
|
36
|
+
Provides-Extra: gadm
|
|
37
|
+
Requires-Dist: geopandas>=0.9.0; extra == "gadm"
|
|
38
|
+
Provides-Extra: all
|
|
39
|
+
Requires-Dist: cartopy>=0.18.0; extra == "all"
|
|
40
|
+
Requires-Dist: geopandas>=0.9.0; extra == "all"
|
|
41
|
+
Requires-Dist: fiona>=1.8.20; extra == "all"
|
|
42
|
+
Requires-Dist: shapely>=1.7.1; extra == "all"
|
|
43
|
+
Requires-Dist: rioxarray>=0.5.0; extra == "all"
|
|
44
|
+
Requires-Dist: owslib>=0.24.1; extra == "all"
|
|
45
|
+
Requires-Dist: rasterio>=1.2.3; extra == "all"
|
|
46
|
+
Requires-Dist: pyproj>=3.0.1; extra == "all"
|
|
47
|
+
Requires-Dist: netCDF4>=1.5.7; extra == "all"
|
|
48
|
+
Requires-Dist: tqdm>=4.60.0; extra == "all"
|
|
49
|
+
Requires-Dist: scipy>=1.6.3; extra == "all"
|
|
28
50
|
Dynamic: author
|
|
29
51
|
Dynamic: author-email
|
|
30
52
|
Dynamic: description
|
|
@@ -61,6 +83,8 @@ pip install Cycles-utils
|
|
|
61
83
|
|
|
62
84
|
| Cycles-utils version | Cycles version |
|
|
63
85
|
| -------------------- | -------------- |
|
|
86
|
+
| 4.2.0 | 1.5.20+ |
|
|
87
|
+
| 4.1.0 | 1.5.20+ |
|
|
64
88
|
| 4.0.6 | 1.5.20 |
|
|
65
89
|
| 4.0.5 | 1.5.20 |
|
|
66
90
|
| 4.0.4 | 1.5.20 |
|
|
@@ -8,12 +8,15 @@ Cycles_utils.egg-info/dependency_links.txt
|
|
|
8
8
|
Cycles_utils.egg-info/requires.txt
|
|
9
9
|
Cycles_utils.egg-info/top_level.txt
|
|
10
10
|
cycles/__init__.py
|
|
11
|
+
cycles/_base_file.py
|
|
11
12
|
cycles/cycles.py
|
|
12
13
|
cycles/cycles_runner.py
|
|
13
14
|
cycles/rotation_builder.py
|
|
14
15
|
cycles/cycles_tools/__init__.py
|
|
15
|
-
cycles/cycles_tools/
|
|
16
|
+
cycles/cycles_tools/_geo_file.py
|
|
16
17
|
cycles/cycles_tools/control_file.py
|
|
18
|
+
cycles/cycles_tools/converter.py
|
|
19
|
+
cycles/cycles_tools/crop_file.py
|
|
17
20
|
cycles/cycles_tools/nudge_file.py
|
|
18
21
|
cycles/cycles_tools/operation_file.py
|
|
19
22
|
cycles/cycles_tools/output_file.py
|
|
@@ -41,4 +44,5 @@ cycles/soilgrids/soilgrids.py
|
|
|
41
44
|
cycles/ssurgo/__init__.py
|
|
42
45
|
cycles/ssurgo/ssurgo.py
|
|
43
46
|
cycles/weather/__init__.py
|
|
44
|
-
cycles/weather/weather.py
|
|
47
|
+
cycles/weather/weather.py
|
|
48
|
+
test/test_runner.py
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
pandas>=1.2.4
|
|
2
|
+
numpy>=1.19.5
|
|
3
|
+
matplotlib>=3.4.2
|
|
4
|
+
tqdm>=4.60.0
|
|
5
|
+
IPython>=7.0.0
|
|
6
|
+
|
|
7
|
+
[all]
|
|
8
|
+
cartopy>=0.18.0
|
|
9
|
+
geopandas>=0.9.0
|
|
10
|
+
fiona>=1.8.20
|
|
11
|
+
shapely>=1.7.1
|
|
12
|
+
rioxarray>=0.5.0
|
|
13
|
+
owslib>=0.24.1
|
|
14
|
+
rasterio>=1.2.3
|
|
15
|
+
pyproj>=3.0.1
|
|
16
|
+
netCDF4>=1.5.7
|
|
17
|
+
tqdm>=4.60.0
|
|
18
|
+
scipy>=1.6.3
|
|
19
|
+
|
|
20
|
+
[gadm]
|
|
21
|
+
geopandas>=0.9.0
|
|
22
|
+
|
|
23
|
+
[plot]
|
|
24
|
+
cartopy>=0.18.0
|
|
25
|
+
geopandas>=0.9.0
|
|
26
|
+
fiona>=1.8.20
|
|
27
|
+
shapely>=1.7.1
|
|
28
|
+
|
|
29
|
+
[soilgrids]
|
|
30
|
+
geopandas>=0.9.0
|
|
31
|
+
rioxarray>=0.5.0
|
|
32
|
+
owslib>=0.24.1
|
|
33
|
+
rasterio>=1.2.3
|
|
34
|
+
shapely>=1.7.1
|
|
35
|
+
pyproj>=3.0.1
|
|
36
|
+
|
|
37
|
+
[ssurgo]
|
|
38
|
+
geopandas>=0.9.0
|
|
39
|
+
shapely>=1.7.1
|
|
40
|
+
fiona>=1.8.20
|
|
41
|
+
|
|
42
|
+
[weather]
|
|
43
|
+
netCDF4>=1.5.7
|
|
44
|
+
scipy>=1.6.3
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Cycles-utils
|
|
3
|
-
Version: 4.0
|
|
3
|
+
Version: 4.2.0
|
|
4
4
|
Summary: Python scripts to build Cycles input files and post-process Cycles output files
|
|
5
5
|
Home-page: https://github.com/PSUmodeling/Cycles-utils
|
|
6
6
|
Author: Yuning Shi
|
|
@@ -10,21 +10,43 @@ Requires-Python: >=3.10
|
|
|
10
10
|
Description-Content-Type: text/markdown
|
|
11
11
|
License-File: LICENSE
|
|
12
12
|
Requires-Dist: pandas>=1.2.4
|
|
13
|
-
Requires-Dist: geopandas>=0.9.0
|
|
14
13
|
Requires-Dist: numpy>=1.19.5
|
|
15
|
-
Requires-Dist: cartopy>=0.18.0
|
|
16
14
|
Requires-Dist: matplotlib>=3.4.2
|
|
15
|
+
Requires-Dist: tqdm>=4.60.0
|
|
16
|
+
Requires-Dist: IPython>=7.0.0
|
|
17
|
+
Provides-Extra: plot
|
|
18
|
+
Requires-Dist: cartopy>=0.18.0; extra == "plot"
|
|
19
|
+
Requires-Dist: geopandas>=0.9.0; extra == "plot"
|
|
20
|
+
Requires-Dist: fiona>=1.8.20; extra == "plot"
|
|
21
|
+
Requires-Dist: shapely>=1.7.1; extra == "plot"
|
|
22
|
+
Provides-Extra: weather
|
|
23
|
+
Requires-Dist: netCDF4>=1.5.7; extra == "weather"
|
|
24
|
+
Requires-Dist: scipy>=1.6.3; extra == "weather"
|
|
17
25
|
Provides-Extra: soilgrids
|
|
26
|
+
Requires-Dist: geopandas>=0.9.0; extra == "soilgrids"
|
|
18
27
|
Requires-Dist: rioxarray>=0.5.0; extra == "soilgrids"
|
|
19
28
|
Requires-Dist: owslib>=0.24.1; extra == "soilgrids"
|
|
20
29
|
Requires-Dist: rasterio>=1.2.3; extra == "soilgrids"
|
|
21
30
|
Requires-Dist: shapely>=1.7.1; extra == "soilgrids"
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
Requires-Dist:
|
|
25
|
-
|
|
26
|
-
Requires-Dist:
|
|
27
|
-
|
|
31
|
+
Requires-Dist: pyproj>=3.0.1; extra == "soilgrids"
|
|
32
|
+
Provides-Extra: ssurgo
|
|
33
|
+
Requires-Dist: geopandas>=0.9.0; extra == "ssurgo"
|
|
34
|
+
Requires-Dist: shapely>=1.7.1; extra == "ssurgo"
|
|
35
|
+
Requires-Dist: fiona>=1.8.20; extra == "ssurgo"
|
|
36
|
+
Provides-Extra: gadm
|
|
37
|
+
Requires-Dist: geopandas>=0.9.0; extra == "gadm"
|
|
38
|
+
Provides-Extra: all
|
|
39
|
+
Requires-Dist: cartopy>=0.18.0; extra == "all"
|
|
40
|
+
Requires-Dist: geopandas>=0.9.0; extra == "all"
|
|
41
|
+
Requires-Dist: fiona>=1.8.20; extra == "all"
|
|
42
|
+
Requires-Dist: shapely>=1.7.1; extra == "all"
|
|
43
|
+
Requires-Dist: rioxarray>=0.5.0; extra == "all"
|
|
44
|
+
Requires-Dist: owslib>=0.24.1; extra == "all"
|
|
45
|
+
Requires-Dist: rasterio>=1.2.3; extra == "all"
|
|
46
|
+
Requires-Dist: pyproj>=3.0.1; extra == "all"
|
|
47
|
+
Requires-Dist: netCDF4>=1.5.7; extra == "all"
|
|
48
|
+
Requires-Dist: tqdm>=4.60.0; extra == "all"
|
|
49
|
+
Requires-Dist: scipy>=1.6.3; extra == "all"
|
|
28
50
|
Dynamic: author
|
|
29
51
|
Dynamic: author-email
|
|
30
52
|
Dynamic: description
|
|
@@ -61,6 +83,8 @@ pip install Cycles-utils
|
|
|
61
83
|
|
|
62
84
|
| Cycles-utils version | Cycles version |
|
|
63
85
|
| -------------------- | -------------- |
|
|
86
|
+
| 4.2.0 | 1.5.20+ |
|
|
87
|
+
| 4.1.0 | 1.5.20+ |
|
|
64
88
|
| 4.0.6 | 1.5.20 |
|
|
65
89
|
| 4.0.5 | 1.5.20 |
|
|
66
90
|
| 4.0.4 | 1.5.20 |
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
from .cycles import Cycles
|
|
2
|
+
from .cycles_tools import generate_control_file
|
|
3
|
+
from .cycles_tools import generate_nudge_file
|
|
4
|
+
from .cycles_tools import generate_soil_file
|
|
5
|
+
from .cycles_tools import generate_reinit_file
|
|
6
|
+
from .cycles_tools import read_control_file
|
|
7
|
+
from .cycles_tools import read_crop_file
|
|
8
|
+
from .cycles_tools import generate_crop_file
|
|
9
|
+
from .cycles_tools import read_soil_file
|
|
10
|
+
from .cycles_tools import read_weather_file
|
|
11
|
+
from .cycles_tools import read_output
|
|
12
|
+
from .cycles_tools import read_operation_file
|
|
13
|
+
from .cycles_tools import generate_operation_file
|
|
14
|
+
from .cycles_tools import convert_obsolete_crop_files
|
|
15
|
+
from .cycles_tools import convert_obsolete_operation_files
|
|
16
|
+
from .cycles_tools import convert_obsolete_soil_files
|
|
17
|
+
from .cycles_tools import SoilLayer
|
|
18
|
+
from .cycles_tools import Operation, Planting, Tillage, Harvest, Kill, FixedFertilization, FixedIrrigation, AutoIrrigation
|
|
19
|
+
from .cycles_runner import CyclesRunner
|
|
20
|
+
from .rotation_builder import CyclesRotationBuilder, Crop, CropGroup
|
|
21
|
+
|
|
22
|
+
__all__ = [
|
|
23
|
+
"Cycles",
|
|
24
|
+
"CyclesRunner",
|
|
25
|
+
"CyclesRotationBuilder",
|
|
26
|
+
"Crop",
|
|
27
|
+
"CropGroup",
|
|
28
|
+
"generate_control_file",
|
|
29
|
+
"read_control_file",
|
|
30
|
+
"generate_nudge_file",
|
|
31
|
+
"read_crop_file",
|
|
32
|
+
"generate_crop_file",
|
|
33
|
+
"read_operation_file",
|
|
34
|
+
"generate_operation_file",
|
|
35
|
+
"read_output",
|
|
36
|
+
"generate_soil_file",
|
|
37
|
+
"read_soil_file",
|
|
38
|
+
"read_weather_file",
|
|
39
|
+
"generate_reinit_file",
|
|
40
|
+
"convert_obsolete_crop_files",
|
|
41
|
+
"convert_obsolete_operation_files",
|
|
42
|
+
"convert_obsolete_soil_files",
|
|
43
|
+
"SoilLayer",
|
|
44
|
+
"Operation", "Planting", "Tillage", "Harvest", "Kill",
|
|
45
|
+
"FixedFertilization", "FixedIrrigation", "AutoIrrigation",
|
|
46
|
+
"plot_yield",
|
|
47
|
+
"plot_operations",
|
|
48
|
+
"plot_map",
|
|
49
|
+
"plot_satellite_map",
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
# Plotting helpers require the optional [plot] extra (cartopy, geopandas,
|
|
53
|
+
# fiona). Import lazily so `import cycles` never requires them.
|
|
54
|
+
_PLOT_ATTRS = ("plot_yield", "plot_operations", "plot_map", "plot_satellite_map")
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def __getattr__(name: str):
|
|
58
|
+
if name in _PLOT_ATTRS:
|
|
59
|
+
from . import cycles_tools
|
|
60
|
+
value = getattr(cycles_tools, name)
|
|
61
|
+
globals()[name] = value
|
|
62
|
+
return value
|
|
63
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def __dir__():
|
|
67
|
+
return sorted(list(globals().keys()) + list(_PLOT_ATTRS))
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
import types
|
|
5
|
+
from dataclasses import fields
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Union, Any
|
|
8
|
+
|
|
9
|
+
def _fmt_float(value: float, digits: int=2) -> str:
|
|
10
|
+
if value == -999.0:
|
|
11
|
+
return '-999'
|
|
12
|
+
for d in range(1, digits):
|
|
13
|
+
if value == round(value, d):
|
|
14
|
+
return f'{value:.{d}f}'
|
|
15
|
+
else:
|
|
16
|
+
return f'{value:.{digits}f}'
|
|
17
|
+
|
|
18
|
+
# Convenience lambdas for metadata
|
|
19
|
+
FMT_1F = lambda v: _fmt_float(v, 1)
|
|
20
|
+
FMT_2F = lambda v: _fmt_float(v, 2)
|
|
21
|
+
FMT_3F = lambda v: _fmt_float(v, 3)
|
|
22
|
+
FMT_4F = lambda v: _fmt_float(v, 4)
|
|
23
|
+
|
|
24
|
+
def _format_field(object, attribute, widths: tuple[int, int], value=None) -> str:
|
|
25
|
+
fmt = attribute.metadata.get('fmt')
|
|
26
|
+
description = attribute.metadata.get('description', '')
|
|
27
|
+
|
|
28
|
+
if value is None:
|
|
29
|
+
value = getattr(object, attribute.name)
|
|
30
|
+
|
|
31
|
+
formatted = fmt(value) if fmt is not None else str(value)
|
|
32
|
+
|
|
33
|
+
return f'{attribute.name.upper():<{widths[0]}}{formatted:<{widths[1]}}# {description}' if description else f'{attribute.name.upper():<{widths[0]}}{formatted}'
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _format_block(label: str, object, *, widths: tuple[int, int]=(28, 8), center: bool=False, all_caps: bool=True) -> str:
|
|
37
|
+
label = label.replace("_", " ").upper() if all_caps else label.replace("_", " ").capitalize()
|
|
38
|
+
|
|
39
|
+
lines = [f' ' * (28 if center else 0) + f'## {label} ##']
|
|
40
|
+
for f in fields(object):
|
|
41
|
+
lines.append(_format_field(object, f, widths))
|
|
42
|
+
lines.append('')
|
|
43
|
+
|
|
44
|
+
return '\n'.join(lines)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _write_file(fn: Path, config) -> None:
|
|
48
|
+
content = '\n'.join(
|
|
49
|
+
_format_block(f.name, getattr(config, f.name))
|
|
50
|
+
for f in fields(config)
|
|
51
|
+
if getattr(config, f.name) is not None
|
|
52
|
+
)
|
|
53
|
+
fn.write_text(content)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _resolve_dict_values(user_dict: dict, simulation: dict[str, Any] | None) -> dict:
|
|
57
|
+
return {key: func(simulation) if callable(func) else func for key, func in user_dict.items()}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _extract(dc_class, resolved: dict) -> dict:
|
|
61
|
+
return {f.name: resolved[f.name] for f in fields(dc_class) if f.name in resolved}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _parse_value(raw: str, name: str, hint: type) -> int | float | str:
|
|
65
|
+
hint = _unwrap_optional(hint)
|
|
66
|
+
if raw.split()[0].lower() == name.lower():
|
|
67
|
+
if hint is int: return int(raw.split()[1])
|
|
68
|
+
if hint is float: return float(raw.split()[1])
|
|
69
|
+
return raw.split()[1]
|
|
70
|
+
else:
|
|
71
|
+
raise ValueError(f"Expected field name '{name}' not found in line: {raw}")
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _unwrap_optional(t) -> type:
|
|
75
|
+
origin = getattr(t, '__origin__', None)
|
|
76
|
+
if origin is Union or origin is types.UnionType or isinstance(t, types.UnionType):
|
|
77
|
+
return next(arg for arg in t.__args__ if arg is not type(None))
|
|
78
|
+
return t
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _if_ipython() -> bool:
|
|
82
|
+
try:
|
|
83
|
+
from IPython import get_ipython # type: ignore
|
|
84
|
+
shell = get_ipython()
|
|
85
|
+
return shell is not None and shell.__class__.__name__ == 'ZMQInteractiveShell'
|
|
86
|
+
except ImportError:
|
|
87
|
+
return False
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _disable_progress_bar() -> bool:
|
|
91
|
+
if _if_ipython():
|
|
92
|
+
return False
|
|
93
|
+
if os.environ.get('SLURM_JOB_ID') is not None:
|
|
94
|
+
return True
|
|
95
|
+
try:
|
|
96
|
+
return not sys.stderr.isatty()
|
|
97
|
+
except AttributeError:
|
|
98
|
+
return True
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
import numpy as np
|
|
3
|
-
import os
|
|
4
3
|
import pandas as pd
|
|
5
|
-
import subprocess
|
|
6
4
|
from collections.abc import Collection
|
|
7
5
|
from dataclasses import dataclass, field
|
|
8
6
|
from pathlib import Path
|
|
@@ -14,8 +12,6 @@ from .cycles_tools import read_weather_file as _read_weather_file
|
|
|
14
12
|
from .cycles_tools import read_output as _read_output
|
|
15
13
|
from .cycles_tools import read_operation_file as _read_operation_file
|
|
16
14
|
from .cycles_tools import generate_reinit_file as _generate_reinit_file
|
|
17
|
-
from .cycles_tools import plot_yield as _plot_yield
|
|
18
|
-
from .cycles_tools import plot_operations as _plot_operations
|
|
19
15
|
|
|
20
16
|
@dataclass
|
|
21
17
|
class Output:
|
|
@@ -26,13 +22,12 @@ class Output:
|
|
|
26
22
|
class Cycles:
|
|
27
23
|
"""Interface for executing one Cycles simulation and reading its files.
|
|
28
24
|
|
|
29
|
-
Provides methods to
|
|
30
|
-
|
|
25
|
+
Provides methods to read outputs, and inspect soil/weather/operation configurations. Automatically loads the control
|
|
26
|
+
file upon initialization.
|
|
31
27
|
|
|
32
28
|
Args:
|
|
33
29
|
path: Path to the simulation directory (containing input/ and output/ subdirs).
|
|
34
30
|
simulation: Name of the simulation (base name of control file without extension).
|
|
35
|
-
executable: Optional absolute path to the Cycles executable binary. This is required to run simulations using the `run` method.
|
|
36
31
|
|
|
37
32
|
Attributes:
|
|
38
33
|
path: Path to the simulation directory (containing input/ and output/ subdirs).
|
|
@@ -44,12 +39,10 @@ class Cycles:
|
|
|
44
39
|
curve_number: Runoff curve number for hydrologic calculations.
|
|
45
40
|
slope: Land slope used in erosion and runoff models.
|
|
46
41
|
weather: DataFrame of weather forcing data (temperature, precipitation, etc.).
|
|
47
|
-
executable: Absolute path to the Cycles executable binary.
|
|
48
42
|
"""
|
|
49
43
|
|
|
50
|
-
path: Path | str
|
|
51
44
|
simulation: str
|
|
52
|
-
|
|
45
|
+
path: Path | str = '.'
|
|
53
46
|
output: dict[str, Output] = field(init=False, default_factory=dict[str, Output])
|
|
54
47
|
control: ControlConfig | None = field(init=False, default=None)
|
|
55
48
|
operations: list | None = field(init=False, default=None)
|
|
@@ -63,36 +56,6 @@ class Cycles:
|
|
|
63
56
|
self.path = Path(self.path)
|
|
64
57
|
assert isinstance(self.path, Path)
|
|
65
58
|
self.control = _read_control_file(self.path / 'input' / f'{self.simulation}.ctrl')
|
|
66
|
-
if self.executable is not None:
|
|
67
|
-
self.executable = str(Path(self.executable).resolve())
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
def run(self, options: str, silence: bool=False) -> tuple[int, str]:
|
|
71
|
-
"""Run the Cycles executable for this simulation.
|
|
72
|
-
|
|
73
|
-
To use the `run` method, the `executable` attribute must be set to the path of the Cycles executable. The
|
|
74
|
-
`options` string is passed directly to the command line when invoking Cycles, allowing you to specify any
|
|
75
|
-
command-line options supported by Cycles (e.g., `-s` for spin-up, etc.).
|
|
76
|
-
|
|
77
|
-
Args:
|
|
78
|
-
options: Command-line options passed to Cycles.
|
|
79
|
-
silence: If True, suppress stdout and stderr printing.
|
|
80
|
-
|
|
81
|
-
Returns:
|
|
82
|
-
A tuple with process return code and stdout text.
|
|
83
|
-
"""
|
|
84
|
-
cmd = [self.executable, *(options.split() if options else []), self.simulation]
|
|
85
|
-
result = subprocess.run(
|
|
86
|
-
cmd,
|
|
87
|
-
shell=os.name == 'nt',
|
|
88
|
-
capture_output=True,
|
|
89
|
-
text=True,
|
|
90
|
-
)
|
|
91
|
-
if not silence:
|
|
92
|
-
print(result.stdout)
|
|
93
|
-
if result.stderr:
|
|
94
|
-
print(result.stderr)
|
|
95
|
-
return result.returncode, result.stdout
|
|
96
59
|
|
|
97
60
|
|
|
98
61
|
def read_output(self, output_types: Collection) -> None:
|
|
@@ -167,6 +130,7 @@ class Cycles:
|
|
|
167
130
|
if 'harvest' not in self.output:
|
|
168
131
|
self.read_output('harvest')
|
|
169
132
|
|
|
133
|
+
from .cycles_tools import plot_yield as _plot_yield # lazy: optional [plot] extra
|
|
170
134
|
return _plot_yield(self.output['harvest'].data, ax=ax, crop_colors=crop_colors, fontsize=fontsize)
|
|
171
135
|
|
|
172
136
|
|
|
@@ -188,4 +152,5 @@ class Cycles:
|
|
|
188
152
|
|
|
189
153
|
rotation_size = self.control.simulation_years.rotation_size
|
|
190
154
|
|
|
155
|
+
from .cycles_tools import plot_operations as _plot_operations # lazy: optional [plot] extra
|
|
191
156
|
return _plot_operations(self.operations, rotation_size, axs=axs, fontsize=fontsize)
|