Interface for executing one Cycles simulation and reading its files.
Provides methods to run simulations, read outputs, and inspect soil/weather/operation configurations. Automatically
loads the control file upon initialization.
| Attributes: |
-
path
(Path | str)
–
Path to the simulation directory (containing input/ and output/ subdirs).
-
simulation
(str)
–
Name of the simulation (base name of control file without extension).
-
output
(dict[str, Output])
–
Dictionary mapping output table names to Output objects with data and units.
-
control
(ControlConfig | None)
–
Parsed control file configuration from the input directory.
-
operations
(list | None)
–
List of parsed operation records from the operation file.
-
soil_profile
(list[SoilLayer] | None)
–
List of SoilLayer objects describing the soil profile.
-
curve_number
(int | None)
–
Runoff curve number for hydrologic calculations.
-
slope
(float | None)
–
Land slope used in erosion and runoff models.
-
weather
(DataFrame | None)
–
DataFrame of weather forcing data (temperature, precipitation, etc.).
-
executable
(Path | str | None)
–
Absolute path to the Cycles executable binary.
|
run(options, silence=False)
Run the Cycles executable for this simulation.
| Parameters: |
-
options
(str)
–
Command-line options passed to Cycles.
-
silence
(bool, default:
False
)
–
If True, suppress stdout and stderr printing.
|
| Returns: |
-
tuple[int, str]
–
A tuple with process return code and stdout text.
|
Source code in cycles/cycles.py
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86 | def run(self, options: str, silence: bool=False) -> tuple[int, str]:
"""Run the Cycles executable for this simulation.
Args:
options: Command-line options passed to Cycles.
silence: If True, suppress stdout and stderr printing.
Returns:
A tuple with process return code and stdout text.
"""
cmd = [self.executable, *(options.split() if options else []), self.simulation]
result = subprocess.run(
cmd,
shell=os.name == 'nt',
capture_output=True,
text=True,
)
if not silence:
print(result.stdout)
if result.stderr:
print(result.stderr)
return result.returncode, result.stdout
|
read_output(output_types)
Read one or more output tables into memory.
| Parameters: |
-
output_types
(Collection)
–
Output table name or collection of names.
|
Source code in cycles/cycles.py
89
90
91
92
93
94
95
96
97
98
99
100
101 | def read_output(self, output_types: Collection) -> None:
"""Read one or more output tables into memory.
Args:
output_types: Output table name or collection of names.
"""
assert isinstance(self.path, Path)
if isinstance(output_types, str):
output_types = output_types,
for output_type in output_types:
df, units = _read_output(self.path / 'output' / self.simulation, output_type)
self.output[output_type] = Output(data=df, units=units)
|
read_operation_file()
Load operation records defined in the control file.
Source code in cycles/cycles.py
| def read_operation_file(self) -> None:
"""Load operation records defined in the control file."""
assert isinstance(self.path, Path)
assert self.control is not None
self.operations = _read_operation_file(self.path / 'input' / self.control.input_files.operation_file)
|
read_soil_file()
Load soil profile layers and metadata from the configured soil file.
Source code in cycles/cycles.py
111
112
113
114
115
116
117 | def read_soil_file(self) -> None:
"""Load soil profile layers and metadata from the configured soil file."""
assert isinstance(self.path, Path)
assert self.control is not None
self.soil_profile, meta = _read_soil_file(self.path / 'input' / self.control.input_files.soil_file)
self.curve_number = meta['curve_number']
self.slope = meta['slope']
|
read_weather_file(*, start_year=-9999, end_year=9999, subdaily=False)
Read weather forcing data for the configured weather file.
| Parameters: |
-
start_year
(int, default:
-9999
)
–
Inclusive first year to keep.
-
end_year
(int, default:
9999
)
–
Inclusive last year to keep.
-
subdaily
(bool, default:
False
)
–
If True, parse hourly format instead of daily.
|
Source code in cycles/cycles.py
120
121
122
123
124
125
126
127
128
129
130 | def read_weather_file(self, *, start_year: int=-9999, end_year: int=9999, subdaily: bool=False) -> None:
"""Read weather forcing data for the configured weather file.
Args:
start_year: Inclusive first year to keep.
end_year: Inclusive last year to keep.
subdaily: If True, parse hourly format instead of daily.
"""
assert isinstance(self.path, Path)
assert self.control is not None
self.weather = _read_weather_file(self.path / 'input' / self.control.input_files.weather_file, start_year=start_year, end_year=end_year, subdaily=subdaily)
|
generate_reinit_file(doy, *, reinit=None)
Generate a reinitialization file from model output.
| Parameters: |
-
doy
(int)
–
Day-of-year to extract from reinit output.
-
reinit
(str | None, default:
None
)
–
Optional output stem for the reinit file.
|
Source code in cycles/cycles.py
133
134
135
136
137
138
139
140
141 | def generate_reinit_file(self, doy: int, *, reinit: str | None=None) -> None:
"""Generate a reinitialization file from model output.
Args:
doy: Day-of-year to extract from reinit output.
reinit: Optional output stem for the reinit file.
"""
assert isinstance(self.path, Path)
_generate_reinit_file(self.path / 'input' / f'{self.simulation if reinit is None else reinit}.reinit', self.path / 'output' / self.simulation, doy)
|
plot_yield(*, ax=None, fontsize=None)
Plot grain and forage yields from harvest output.
Source code in cycles/cycles.py
| def plot_yield(self, *, ax: Axes | None=None, fontsize: int | None=None) -> Axes:
"""Plot grain and forage yields from harvest output."""
if 'harvest' not in self.output:
self.read_output('harvest')
return _plot_yield(self.output['harvest'].data, ax=ax, fontsize=fontsize)
|
plot_operations(*, axs=None, fontsize=None)
Plot operation timelines grouped by rotation year.
| Parameters: |
-
rotation_size
–
Number of years in the plotted rotation.
-
axs
(Axes | ndarray | None, default:
None
)
–
Optional axes object(s) to draw on.
-
fontsize
(int | None, default:
None
)
–
Global matplotlib font size override.
|
| Returns: |
-
–
The axes used for plotting.
|
Source code in cycles/cycles.py
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171 | def plot_operations(self, *, axs: Axes | np.ndarray | None=None, fontsize: int | None=None):
"""Plot operation timelines grouped by rotation year.
Args:
rotation_size: Number of years in the plotted rotation.
axs: Optional axes object(s) to draw on.
fontsize: Global matplotlib font size override.
Returns:
The axes used for plotting.
"""
if self.operations is None:
self.read_operation_file()
assert self.control is not None
assert self.operations is not None
rotation_size = self.control.simulation_years.rotation_size
return _plot_operations(self.operations, rotation_size, axs=axs, fontsize=fontsize)
|