Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
daquinterop committed Nov 15, 2024
2 parents b03eb08 + a8106b1 commit c9d9e14
Show file tree
Hide file tree
Showing 9 changed files with 43 additions and 27 deletions.
2 changes: 1 addition & 1 deletion DSSATTools/base/sections.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
'Tomato': 'A6,1X,A20,1X,A1,1X,A6,2(1X,F5.2),3(1X,F5.1),3(1X,F5.2),1X,F5.0,1X,F5.1,1X,F5.2,1X,F5.4,4(1X,F5.1),2(1X,F5.3)',
'Cabbage': 'A6,1X,A16,1X,A5,1X,A6,1X,F5.2,1X,F5.3,3(1X,F5.1),2(1X,F5.2),1X,F5.3,1X,F5.0,1X,F5.1,1X,F5.3,1X,F5.2,1X,F5.1,1X,F5.2,2(1X,F5.1),2(1X,F5.3)',
'Sugarcane': 'A6,1X,A16,1X,A5,1X,A6,22(1X,F14.4)',
"Wheat": "A6,1X,A16,1X,A5,1X,A6,5(1X,F5.1),1X,F5.2,1X,F5.1"
"Wheat": "A6,1X,A16,1X,A5,1X,A6,5(1X,F5.1),1X,F5.2,1X,F5.0"
}
ECOTYPE_HEADER_FMT = {
'Maize': 'A5,1X,1X,A16,1X,11(1X,A5)',
Expand Down
4 changes: 2 additions & 2 deletions DSSATTools/crop.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,9 +172,9 @@ def available_cultivars(crop_name):
cul_path = os.path.join(GENOTYPE_PATH, f'{CODE}{SMODEL[2:]}{VERSION}.CUL')
with open(cul_path, "r") as f:
lines = f.readlines()
lines = [l for l in lines if l[:1] not in ["@", "*", "!"]]
lines = [l for l in lines if l[:1] not in ["@", "*", "!", "$"]]
lines = [l for l in lines if len(l) > 5]
return [l.split()[0] for l in lines]
return [l.split()[0] for l in lines if len(l.strip()) > 6]

class Crop:
def __init__(self, crop_name:str='Maize', cultivar_code:str=None):
Expand Down
4 changes: 2 additions & 2 deletions DSSATTools/management.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ def __init__(
'table': TabularSubsection({
'IDATE': [planting_date.strftime('%y%j'),],
'IROP': ['IR001',],
'IVAL': [0,]
'IRVAL': [0,]
})
}
)
Expand Down Expand Up @@ -314,7 +314,7 @@ def write(self, filename='EXP', expname='DEFAULT'):

outstr += '*TREATMENTS -------------FACTOR LEVELS------------\n'
outstr += '@N R O C TNAME.................... CU FL SA IC MP MI MF MR MC MT ME MH SM\n'
self._treatmentOptions["MI"] = min(1, self.irrigation['table']["IVAL"].sum())
self._treatmentOptions["MI"] = min(1, self.irrigation['table']["IRVAL"].sum())
self._treatmentOptions["MF"] = min(1, self.fertilizers["table"][["FAMN", "FAMP", "FAMK", "FAMC", "FAMO"]].values.max())
self._treatmentOptions["MH"] = min(1, int(self.harvest_details["HDATE"] is not None))
outstr += f' 1 1 0 0 DEFAULT TREATMENT {" ".join(map(str, IMPLEMENTED_SECTIONS.values()))}\n\n'
Expand Down
36 changes: 27 additions & 9 deletions DSSATTools/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,22 +34,35 @@
from DSSATTools.base.sections import TabularSubsection

OS = platform.system().lower()
OUTPUTS = ['PlantGro', "Weather", "SoilWat", "SoilOrg"]
OUTPUTS = ['PlantGro', "Weather", "SoilWat", "SoilOrg", "SoilNi"]
OUTPUT_MAP = {
"PlantGro": "GROUT", "SoilWat": "WAOUT", "SoilOrg": "CAOUT",
"Weather": "GROUT"
"Weather": "GROUT", "SoilNi": "NIOUT"
}
SOIL_LAYER_OUTPUTS = ["SoilNi"]

PERENIAL_FORAGES = ['Alfalfa', 'Bermudagrass', 'Brachiaria', 'Bahiagrass']
ROOTS = ['Potato']

# Paths to DSSAT and Env variables
BASE_PATH = os.path.dirname(module_path)
STATIC_PATH = os.path.join(BASE_PATH, 'static')
STATIC_PATH = STATIC_PATH + os.sep
STD_PATH = os.path.join(STATIC_PATH, 'StandardData')
CRD_PATH = os.path.join(STATIC_PATH, 'Genotype')
SLD_PATH = os.path.join(STATIC_PATH, 'Soil')
TMP = tempfile.gettempdir()
DSSAT_HOME = os.path.join(TMP, f"DSSAT{VERSION}"+os.sep)
STD_PATH = os.path.join(DSSAT_HOME, 'StandardData')
CRD_PATH = os.path.join(DSSAT_HOME, 'Genotype')
SLD_PATH = os.path.join(DSSAT_HOME, 'Soil')

# Creates a folder with DSSAT files. This is done to avoid long path names
# that exceed the defined lenght for path variables in DSSAT.
if not os.path.exists(DSSAT_HOME):
os.mkdir(DSSAT_HOME)
for file in os.listdir(STATIC_PATH):
file_link = os.path.join(DSSAT_HOME, file)
if os.path.exists(file_link):
os.remove(file_link)
os.symlink(os.path.join(STATIC_PATH, file), file_link)

if 'windows'in OS:
BIN_PATH = os.path.join(STATIC_PATH, 'bin', 'dscsm048.exe')
CONFILE = 'DSSATPRO.V48'
Expand Down Expand Up @@ -225,14 +238,14 @@ def run(self,
else:
f.write(f'M{crop._CODE} {self._RUN_PATH} dscsm048 {crop._SMODEL}{VERSION}\n')
f.write(f'CRD {CRD_PATH}\n')
f.write(f'PSD {os.path.join(STATIC_PATH, "Pest")}\n')
f.write(f'PSD {os.path.join(DSSAT_HOME, "Pest")}\n')
f.write(f'SLD {SLD_PATH}\n')
f.write(f'STD {STD_PATH}\n')

exc_args = [BIN_PATH, 'C', os.path.basename(management_filename), '1']
excinfo = subprocess.run(exc_args,
cwd=self._RUN_PATH, capture_output=True, text=True,
env={"DSSAT_HOME": STATIC_PATH, }
env={"DSSAT_HOME": DSSAT_HOME, }
)
excinfo.stdout = re.sub("\n{2,}", "\n", excinfo.stdout)
excinfo.stdout = re.sub("\n$", "", excinfo.stdout)
Expand All @@ -259,16 +272,21 @@ def run(self,
assert f'{file}.OUT' in OUTPUT_FILES, \
f'{file}.OUT does not exist in {self._RUN_PATH}'
table_start = -1
init_lines = []
with open(os.path.join(self._RUN_PATH, f'{file}.OUT'), 'r', encoding='cp437') as f:
while True:
table_start += 1
if '@' in f.readline():
init_lines.append(f.readline())
if '@' in init_lines[-1][:10]:
break

try:
df = pd.read_csv(
os.path.join(self._RUN_PATH, f'{file}.OUT'),
skiprows=table_start, sep=' ', skipinitialspace=True
)
df = df.dropna(how="all", axis=1)

except UnicodeDecodeError:
with open(os.path.join(self._RUN_PATH, f'{file}.OUT'), 'r', encoding='cp437') as f:
lines = f.readlines()
Expand Down
4 changes: 2 additions & 2 deletions DSSATTools/weather.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ def __init__(self, df:DataFrame, pars:dict, lat:float=None, lon:float=None,
'''
Initialize a Weather instance. This instance contains the weather data,
as well as the parameters that define the weather station that the data
represents,nsuch as the latitude, longitude and elevation.
represents, such as the latitude, longitude and elevation.
Arguments
----------
Expand Down Expand Up @@ -212,7 +212,7 @@ def write(self, folder:str='', **kwargs):
])
outstr += weather_data_header(self.data.columns)

df = self.data.applymap(lambda x: f"{x:5.1f}")
df = self.data.map(lambda x: f"{x:5.1f}")
df['day'] = df.index.strftime("%Y%j")
df = df [["day"]+list(self.data.columns)]
outstr += "\n".join(map(lambda x: " ".join(x), df.values))
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ pip install DSSATTools
```
## v2.1 Updates
For the latest version the next changes were implemented:
- Everything is based in the fact that each simulation includes only one treatment. That involves a single option for cultivars, irrigation, fertilizer, field, etc.
- The library simultes only one treatment. Therefore, only one option for cultivars, irrigation, fertilizer, field, etc. can be defined.
- Every set of defined crop or management parameters is a section. Each section is an attribute of the `Crop` or `Management` class. Sections won't be created by the user. The user can only modify the value of the parameters of the section, they can't create or add new parameters.
- The weather is now managed by a single `Weather` class.
- A `__repr__` method was implemented for the four basic classes (`Crop`, `Management`, `Weather` and `SoilProfile`), and the `Section` class.
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

setuptools.setup(
name="DSSATTools",
version="2.1.2",
version="2.1.4",
author="Diego Quintero",
author_email="[email protected]",
description="A DSSAT's Python implementation",
Expand Down
4 changes: 2 additions & 2 deletions tests/test_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,7 @@ def test_run_soybean():
man.irrigation["table"] = TabularSubsection({
'IDATE': ["85183", "85189", "85196", "85200", "85214", "85221"],
'IROP': ["IR001"]*6,
'IVAL': [41, 54, 56, 51, 40, 25],
'IRVAL': [41, 54, 56, 51, 40, 25],
})
# *SIMULATION CONTROLS
# @N GENERAL NYERS NREPS START SDATE RSEED SNAME.................... SMODEL
Expand Down Expand Up @@ -569,7 +569,7 @@ def test_run_tomato():
'94170','94171','94172','94173','94174','94175'
],
'IROP': ["IR007"]*126,
'IVAL': [
'IRVAL': [
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,3,3,4,3,3,4,4,3,3,3,4,
4,4,3,4,4,4,4,4,4,5,4,4,4,4,4,4,4,4,3,3,4,4,3,4,4,4,5,5,4,5,5,5,
5,5,5,5,4,5,4,5,4,4,5,5,5,3,3,5,4,5,5,5,5,5,5,5,5,4,4,5,5,3,2,2,
Expand Down
12 changes: 5 additions & 7 deletions tests/test_weather.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,9 @@
import numpy as np
import shutil
import os
import platform
if 'windows' in platform.system().lower():
BASE_PATH = 'C:/Users/daqui/'
else:
BASE_PATH='/home/diego'
from pathlib import Path

PROJECT_ROOT = Path(__file__).parent.parent

DATES = pd.date_range('2000-01-01', '2010-12-31')
N = len(DATES)
Expand All @@ -34,7 +32,7 @@

class TestWeather:
def test_write(self):
folder = os.path.join(BASE_PATH, 'Py_DSSATTools', 'wth_test')
folder = os.path.join(PROJECT_ROOT, 'tests', 'wth_test')
if os.path.exists(folder): shutil.rmtree(folder)
wth = Weather(df, variables, 4.54, -75.1, 1800)
wth.write(folder)
Expand Down Expand Up @@ -157,4 +155,4 @@ def test_co2_value(self):
dssat.run(
soil, wth, crop, man
)
assert np.isclose(dssat.output["Weather"]["CO2D"].iloc[0], 500., atol=1)
assert np.isclose(dssat.output["Weather"]["CO2D"].iloc[0], 500., atol=1)

0 comments on commit c9d9e14

Please sign in to comment.