Advanced elements¶
Base models inherited by all pySWAP models.
A lot of functionality can be abstracted away in the base models. This way, the code is more DRY and easier to maintain. The base models are used to enforce the correct data types and structure of the input data. They also provide methods to convert the data to the format required by the SWAP model.
Classes defined here are based on Pydantic BaseModel and Pandera DataFrameModel. Both are meant to ensure the correct data types and structure of the input data, as successful validation means smooth execution of the SWAP model. Particularily important when run as a submitted job on an HPC.
Classes:
Name | Description |
---|---|
BaseModel |
Base class for pySWAP models. Inherits from Pydantic BaseModel. |
BaseTableModel |
Base class for pySWAP models that validate pandas DataFrames. Inherits from Pandera DataFrameModel. |
BaseTableModel
¶
Bases: DataFrameModel
Base model for pandas DataFrames.
Methods:
Name | Description |
---|---|
create |
Create a validated DataFrame from a dictionary. |
Source code in pyswap/core/basemodel.py
PySWAPBaseModel
¶
Bases: BaseModel
Base class for pySWAP models.
Methods:
Name | Description |
---|---|
__setattr__ |
Overriden method to silently ignore assignment of frozen fields. |
update |
Update the model with new values from a dictionary. |
Source code in pyswap/core/basemodel.py
__setattr__(name, value)
¶
Silently ignore assignment of frozen fields.
This method is overridden to silently ignore assignment of frozen fields to avoid errors when an old swp files is read.
Source code in pyswap/core/basemodel.py
convert_switches(value, info)
classmethod
¶
Convert switch values to integers.
This method was necessary to ensure that loading models from ASCII files would work. It could be improved to include literals that do not start with "sw" as well.
Source code in pyswap/core/basemodel.py
update(new, inplace=False, no_validate=False)
¶
Update the model with new values.
Given dictionary of values is first filtered to include only the fields that exist in the model. The model is then updated with the new values. The updated model is returned (either new or updated self).
Parameters:
Name | Type | Description | Default |
---|---|---|---|
new
|
dict
|
Dictionary with new values. |
required |
inplace
|
bool
|
If True, update the model in place. |
False
|
Source code in pyswap/core/basemodel.py
Mixins¶
Reusable mixins enhancing functionality of specific PySWAPBaseModel.
To keep the main PySWAPBaseModel class and the components library clean and focused, mixins are used to add additional functionality to the classes that need it. The concept of the mixins was inspired by the Django framework and it really helps to keep the code clean and organized.
Should more functionality be needed in the future for one or more classes, it should be implemented as a mixin and then inherited by the classes that need it.
Classes:
FileMixin: Custom saving functionality for models that need file I/O.
SerializableMixin: Converting a model to a SWAP-formatted string.
YAMLValidatorMixin: Validating parameters using external YAML rules.
WOFOSTUpdateMixin: Interface for the WOFOST crop parameters database for
pySWAP.
FileMixin
¶
Custom saving functionality for models that need file I/O.
!!! note:
The _extension attribute should be set in the class that inherits
this mixin. It is recommended that pydantic's PrivateAttr is used to
hide this attribute from the user.
Methods:
Name | Description |
---|---|
save_file |
Saves a string to a file. |
Source code in pyswap/utils/mixins.py
save_file(string, fname, path)
¶
Saves a string to a file.
The extension should now be provided in each class inheriting this mixin as a private attribute.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
string
|
str
|
The string to be saved to a file. |
required |
fname
|
str
|
The name of the file. |
required |
path
|
Path
|
The path where the file should be saved. |
required |
Source code in pyswap/utils/mixins.py
SerializableMixin
¶
Bases: BaseModel
Converting a model to a SWAP-formatted string.
This mixin is only inherited by classes that directly serialize to a SWAP-formatted string. The assumptions are that the inheriting classes:
- do not contain nested classes.
- if the class contains nested classes it should either use Subsection field
types or override the
model_string()
method.
Methods:
Name | Description |
---|---|
if_is_union_type |
Check if the field type is a Union type. |
is_annotated_exception_type |
Check if the attribute type is Table, Arrays, or ObjectList. |
serialize_model |
Override the default serialization method. |
model_string |
Concatenate the formatted strings from dictionary to one string. |
Source code in pyswap/utils/mixins.py
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 |
|
if_is_union_type(field_info)
¶
Check if the field type is a Union type.
If it is, look for the json_schema_extra attribute in the field_info of the first argument of the Union type. If it is not found, return None. It was necessary in cases of, for example, optional classes like Union[Table, None].
Parameters:
Name | Type | Description | Default |
---|---|---|---|
field_info
|
FieldInfo
|
The FieldInfo object of the field. |
required |
Source code in pyswap/utils/mixins.py
is_annotated_exception_type(field_name)
¶
Check if the attribute type is Table, Arrays, or ObjectList.
For Table, Arrays, and ObjectList types True is returned, ensuring a separate serialization path.
First try to assign the json_schema_extra from a Union type. If that fails, assign the json_schema_extra from the field_info. If the json_schema_extra is None, return False.
Source code in pyswap/utils/mixins.py
model_string(mode='string', **kwargs)
¶
Concatenate the formatted strings from dictionary to one string.
!!! note: By alias is True, because in some cases, particularily in the case of CropSettings, the WOFOST names of parameters in the database were different from those used in SWAP. This allows those parameters to be properly matched, yet serialized properly in SWAP input files.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
mode
|
Literal["str", "list]
|
The output format. |
'string'
|
kwargs
|
dict
|
Additional keyword arguments passed to |
{}
|
Source code in pyswap/utils/mixins.py
serialize_model(handler)
¶
Override the default serialization method.
In the intermediate step, a dictionary is created with SWAP formatted strings.
Source code in pyswap/utils/mixins.py
WOFOSTUpdateMixin
¶
Interface for the WOFOST crop parameters database for pySWAP.
This mixin should be inherited by classes that share parameters with the WOFOST crop database.
Source code in pyswap/utils/mixins.py
update_from_wofost()
¶
Update the model with the WOFOST variety settings.
Source code in pyswap/utils/mixins.py
YAMLValidatorMixin
¶
Bases: BaseModel
A mixin class that provides YAML-based validation for models.
Initially, pySWAP had model serializers on each model component class which had a number of assertions to validate the parameters (i.e., require parameters rlwtb and wrtmax if swrd = 3). This created chaos in the code, and since none of it was used by inspection tools anyways, it was decided to leave the validation logic in the code and move the rules to a separate YAML file.
Methods:
Name | Description |
---|---|
validate_parameters |
Validate parameters against required rules. |
validate_with_yaml |
Pydantic validator executing validation logic. |
Source code in pyswap/utils/mixins.py
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 |
|
validate_parameters(switch_name, switch_value, params, rules)
staticmethod
¶
Validate parameters against required rules.
This method reads the rules for the model from the YAML file and checks if the required parameters are present. If not, it raises a ValueError.
SaltStress: # <--- Model name
swsalinity: # <--- Switch name (switch_name)
1: # <--- Switch value (switch_value)
- saltmax # <---| Required parameters
- saltslope # <--|
2:
- salthead
Parameters:
Name | Type | Description | Default |
---|---|---|---|
switch_name
|
str
|
The name of the switch (e.g., 'swcf'). |
required |
switch_value
|
Any
|
The value of the switch (e.g., 1 or 2). |
required |
params
|
dict
|
Dictionary of parameters to check. |
required |
rules
|
dict
|
Dictionary with validation rules. |
required |
Raises:
Type | Description |
---|---|
ValueError
|
If required parameters are missing. |
Source code in pyswap/utils/mixins.py
validate_with_yaml()
¶
Pydantic validator executing validation logic.
All validators defined on a model run on model instantiation. This method makes sure that YAML validation is postponed until the _validation parameter (required on all classes inheriting this mixin) is set to True. This state is done when all the required parameters are presumed to be set, e.g., when the user tries to run the model.
Source code in pyswap/utils/mixins.py
Validation and serialization¶
Functions to parse SWAP formatted ascii files into pySWAP objects.
pySWAP has the ability to interact directly with the classic SWAP input files.
Parsers defined in this module are used for the custom field validators defined
in the pyswap.core.fields
module. These functions convert (or deserialize) the
SWAP formatted ascii files into pySWAP objects.
Parsers in this module
parse_string_list: Convert a SWAP string list to a list of strings. parse_quoted_string: Make sure to remove unnecessary quotes from source. parse_day_month: Convert a string to a date object with just the day and month.
parse_day_month(value)
¶
Convert a string to a date object with just the day and month.
Source code in pyswap/core/parsers.py
parse_decimal(value)
¶
parse_float_list(value)
¶
Convert a SWAP string list to a list of strings.
parse_int_list(value)
¶
Convert a SWAP string list to a list of strings.
parse_quoted_string(value)
¶
Make sure to remove unnecessary quotes from source.
parse_string_list(value)
¶
Convert a SWAP string list to a list of strings.
Functions to fine tune the serializatino of pySWAP objects to SWAP formatted ASCII.
More complex serialization logic which would be unwieldy to implement directly in the Annotated field definitions (pyswap.core.fields module) as lambda functions are defined in the serializers module (pyswap.core.serializers). These are functions that convert objects to strings in the valid SWAP format.
Serializers in this module
serialize_table: Convert a DataFrame to a string. serialize_arrays: Convert a DataFrame to a string without headers and newline in front. serialize_csv_table: Convert a DataFrame to a string in CSV format. serialize_object_list: Convert a list of objects to a string. serialize_day_month: Convert a date object to a string with just the day and month.
serialize_arrays(table)
¶
Convert the DataFrame to a string without headers and newline in front.
Arguments:
table: The DataFrame to be serialized.
Result:
>>> 'ARRAYS =
1 4 2 5 3 6
'
Source code in pyswap/core/serializers.py
serialize_csv_table(table)
¶
Convert the DataFrame to a string in CSV format.
This serializer is specifically tailored to output the data in the format of the ,met files used in SWAP.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
table
|
DataFrame
|
The DataFrame to be serialized. |
required |
Source code in pyswap/core/serializers.py
serialize_day_month(value)
¶
Serialize a date object to a string with just the day and month.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
value
|
date
|
The date object to be serialized. |
required |
Result
'01 01'
serialize_table(table)
¶
Convert the DataFrame to a string.
Arguments:
table: The DataFrame to be serialized.
Result:
>>> ' A B
1 4 2 5 3 6 '
I/O¶
Interact with the filesystem
All functions that interact with the filesystem are located in this subpackage.
Modules:
Name | Description |
---|---|
io_ascii |
Functions to interact with ASCII files. |
io_yaml |
Functions to interact with YAML files. |
classic_swap |
Functions to load with classic SWAP input files. |
io_ascii
¶
Interact with ASCII files.
Functions:
Name | Description |
---|---|
open_ascii |
Open an ASCII file and detect its encoding. |
save_ascii |
Save a string to an ASCII file. |
open_ascii(file_path)
¶
Open file and detect encoding.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
file_path
|
str
|
Path to the file to be opened. |
required |
Source code in pyswap/core/io/io_ascii.py
save_ascii(string, fname, path, mode='w', extension=None, encoding='ascii')
¶
Saves a string to a file with a given extension.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
string
|
str
|
The string to be saved to a file. |
required |
extension
|
str
|
The extension that the file should have (e.g. 'txt', 'csv', etc.). |
None
|
fname
|
str
|
The name of the file. |
required |
path
|
str
|
The path where the file should be saved. |
required |
mode
|
str
|
The mode in which the file should be opened (e.g. 'w' for write, 'a' for append, etc.). |
'w'
|
encoding
|
str
|
The encoding to use for the file (default is 'ascii'). |
'ascii'
|
Returns:
Type | Description |
---|---|
None
|
None |
Source code in pyswap/core/io/io_ascii.py
CLI¶
Command Line Interface for pySWAP.
This is a prototype subpackage for potential enhancement of pyswap's functionality. CLI tools can be very helpful in automating some tasks, like loading databases or classic SWAP models.
Note
At the moment only creating project structure was prototyped. More functionality will be added in the future if users express such need.
Example:
```cmd
pyswap init --notebook # creates the project structure with a template .ipynb file.
pyswap init --script # creates the project structure with a .py file.
```
After running the script, you will see the following folder created:
test project
├── README
├── __init__.py
├── data
├── models
│ ├── __init__.py
│ └── main.ipynb
└── scripts
└── __init__.py
The __init__.py files are added to create a module structure. Now when you create a python file in scripts with some helper functions,
you can import those functions to the main model script or notebook and use it there.
By default, a git repository is also created along with the project structure.
cli
¶
The cli module is supposed to help in structuring the direcotries of created models and enforce best practices in documenting. It creates a modular structure (with init.py files) what can be helpful when writing scripts. This way, modules from the scripts can be directly imported into the main.py or main.ipynb
init(script=False, notebook=True)
¶
Prompt the user to enter their information and create a User class.