Version 1.5.3
API improvements
Added ``append_simulation`` method
Added a new
append_simulation`()method for appending simulations into the currentApsimModelinstance. Unlikeclone_simulation, this method supports transferring simulations from externalApsimModelobjects in addition to duplicating simulations already present within the current model instance.
Advantages:
Reuse simulations across APSIM models without manual copying.
Build complex scenario collections from multiple source models with minimal effort.
Quickly create simulation variants by duplicating existing simulations.
Avoid naming conflicts through optional simulation renaming.
Improve productivity when assembling large simulation ensembles.
Improves execution efficiency by allowing multiple simulations to be managed within a single APSIM model instance, reducing the overhead associated with repeatedly loading and initializing
Models.exe.Rapidly generate multiple simulation scenarios using the
payloadargument, enabling efficient exploration of management, soil, weather, or cultivar variations and their impacts on model outputs.Simplify what-if analysis by automatically creating simulation variants from different input configurations, reducing the effort required to evaluate alternative strategies and outcomes.
Basic example:
from apsimNGpy import ApsimModel
base_model =ApsimModel('Maize')
other_simulation = ApsimModel("Soybean")[0] # gets the first simulation
base_model.append_simulation(
simulation=other_simulation,
rename="Simulation2"
)
Duplicating an existing simulation:
from apsimNGpy import ApsimModel
model =ApsimModel('Maize')
model.append_simulation(
model[0]
rename="Simulation_copy"
)
Edit the added simulation on the fly:
with ApsimModel('Maize') as model:
model.append_simulation(simulation=model[0], rename='pop12',
payload=dict(model_type='Models.Manager',
model_name='Sow using a variable rule',
Population=12))
.. note::
This method should not be used with ``ExperimentManager`` objects,
even though ``ExperimentManager`` inherits from ``ApsimModel``. These objects supports one base simulation
and may produce unintended behavior when appended directly.
Normally you may need to edit the newly edited simulation surgically to make it unique from the existing simulation but
the method allows editing via key word payload which could be a dict or a list of dicts as follows::
model.append_simulation(fixed_model[0], rename='clone1', [payload=dict(model_type='Models.Manager',
model_name='Sow using a variable rule',
Population=12)])
For additional usage examples and implementation details see::
help(model.append_simulation)
** Added
add_node_frommethod**
Added a utility for transferring nodes between APSIM models with improved control and safety. The method now enforces keyword-only arguments (via
*) to prevent parameter mis-ordering and improve readability.Key features:
Copy nodes from external or internal APSIM models into a specified target location
Optionally delete existing nodes with matching name and type before insertion (
del_if_exists)Support renaming of inserted nodes (
rename)Allow node identification using either full paths or node names
Improved handling of multiple nodes of the same type (e.g., Manager scripts with different roles)
This update enhances robustness model editing and generation and usability for workflows involving model customization, scenario generation, and dynamic node manipulation.
Added clone_simulation method
Added support for cloning existing simulations with a new name, enabling users to easily create and manage multiple simulation scenarios within the same model. This update facilitates comparative analyses (e.g., varying management practices such as fertilization rates) while preserving the original simulation configuration. see doc;
clone_simulation(). see example below:
from apsimNGpy import ApsimModel
model = ApsimModel("Maize")
# Create a new simulation with a different population
population = 10
simulation_name = f"sim_{population}"
model.clone_simulation(
rename=simulation_name,
base_simulation=0,
)
# Update the cloned simulation
model.edit_model(
model_type="Models.Manager",
model_name="Sow using a variable rule",
simulations=simulation_name,
Population=population,
)
# Update the original simulation
model.edit_model(
model_type="Models.Manager",
model_name="Sow using a variable rule",
simulations="Simulation",
Population=4,
)
# Verify that two simulations now exist in the model
model.inspect_model("Simulation")
# Output:
# ['.Simulations.Simulation', '.Simulations.sim_10']
# APSIM assigns a SimulationID in the report tables, but the
# simulation name is often easier to interpret during analysis.
# Add the simulation name to the report output.
model.edit_model(
model_type="Models.Report",
model_name="Report",
variable_spec=[
"[Simulation].Name as SimulationName"
],
)
model.run()
# Compare average yield by simulation
model.results.groupby("SimulationName")["Yield"].mean()
# Output:
#
# SimulationName
# Simulation 4786.670976
# sim_10 6287.776688
# Name: Yield, dtype: float64
Added has_node method
Added a new
has_nodemethod that allows users to check whether a given node name or path exists within the current APSIM model or specified scope. see doc;has_node().
from apsimNGpy import ApsimModel
with ApsimModel('Maize') as model:
has = model.has_node('Sow using a variable rule', 'Models.Manager')
# {'ok': True, 'fullpath': False}
has = model.has_node('.Simulations.Simulation.Field.Sow using a variable rule', "Models.Manager")
# {'ok': True, 'fullpath': True}
Added switch_wm_to_swim3 method
This method to simplifies replacing the default APSIM water balance model with the physically based SWIM3 module.
- New Features
Automatically replaces the existing soil water model with Models.Soils.Swim3
Supports optional subsurface tile drainage configuration
Allows custom SWIM3 parameter overrides through dictionaries
Adds validation for invalid drainage parameter keys
Supports custom layer structure thickness configuration
with this method, sub surface drainage can be declared with automatic configuration:
model.switch_wm_to_swim3(
ss_tile_drainage="auto"
)
or custom settings:
model.switch_wm_to_swim3(
ss_tile_drainage={
"DrainDepth": 1200,
"DrainSpacing": 30000,
"ImpermDepth": 2500
}
)
SWIM3 model parameters can can also be declared as follows:
model.switch_wm_to_swim3(
swim_model_params = {"eo_time": "05:00", "eo_durn": 600.0,
"default_rain_time": "00:00",
"default_rain_duration": 500.0,
"Diagnostics": False
}
)
for more information see:
help(model.switch_wm_to_swim3)
Cultivar editing interface API
Introduced an explicit cultivar editing class;
CultivarEditorusing a composition-based design. Due to limitations in the APSIM API, replacement nodes cannot currently be edited in place. As a workaround, edited cultivars are added directly under the plant’s children rather than modifying the replacement nodes themselves. see doc:CultivarEditor``add_new_model`` method
Added a new utility for dynamically constructing and inserting APSIM model nodes from Python dictionaries. This method simplifies programmatic APSIM model generation and editing while providing improved control over insertion, replacement, and renaming behavior.
Key features:
Create APSIM model nodes directly from Python dictionaries
Support APSIM-standard
"$type"or Python-friendly"type"declarationsAutomatically resolve and instantiate APSIM CLR model types
Insert nodes into arbitrary APSIM parent nodes
Optionally replace existing nodes with matching type and name
Optionally rename inserted nodes
Automatically parse APSIM
Clockdate fieldsAutomatically convert
Manager.Parametersinto compatible .NET structuresGracefully ignore unsupported or incompatible attributes during assignment
Basic example:
from apsimNGpy import ApsimModel
model = ApsimModel("Maize")
model.add_new_model(
parent_identifier="Simulation",
parent_type="Simulation",
source={
"$type": "Models.Clock, Models",
"Start": "2000-01-01",
"End": "2020-12-31"
}
)
Adding a Manager script:
model.add_new_model(
parent_identifier=".Simulations.Simulation.Field",
parent_type="Zone",
source={
"type": "Models.Manager, Models",
"Name": "IrrigationManager",
"Parameters": [
{"Key": "Amount", "Value": 30}
],
"CodeArray": [] # code needed here
},
replace=False,
rename="IrrigationManager_v2"
)
For more information and additional examples see::
help(model.add_new_model)
or
See doc: add_new_model()
Bug Fixes
``add_replacement`` duplication bug
Fixed an issue where running the replacement code multiple times created duplicate replacement nodes. The method now prevents replication of existing replacements.
inspect_model() now return [] if specified model type does non exist in the simulation tree
Removed the fixed 8000-second execution timeout and replaced it with unlimited runtime by default, preventing premature termination of computationally intensive simulations such as Sobol sensitivity analyses.
# model.inspect_model_parameters()` now returns a dictionary consistently across inspected APSIM model types. - A dict allows to explicitly include the scalar values that are not layered in nature..
Version 1.5.2
Improvements
Added lazy-loading support for APSIM runtime modules to prevent early CLR initialization.
Consolidates all apsimNGpy public API into a single access point to enable easier discovery and support lazy importing.
Bug Fixes
Fixed issues with APSIM binary path configuration when importing
apsimNGpy.Improved compatibility with Ruff static analysis for
TYPE_CHECKINGimports.fixed f strings errors in early versions of python
Fixed the add_crop_replacements method and removed the deprecated _crop argument. Future releases will allow this method to be called without arguments, automatically detecting available plant modules and adding them to the replacements folder.
Tests
Added tests verifying lazy attribute resolution and caching behavior.
Added tests ensuring unittest modules are correctly exposed.
Version 1.5
Improvements
Moved
pythonet_configfrom thecoremodule to thestartermodule to improve module organization and streamline initialization flow.Verified compatibility with the latest APSIM binary (build 7990).
Added
__len__toApsimModel:len(model)now returns the number of loaded simulations.Internally delegates to
len(self.simulations).
Added
__getitem__toApsimModel:model[index]returns the simulation at the specified index.model["SimulationName"]returns the simulation matching the given name.
APSIM Runtime Loading Improvements
The apsimNGpy.bin_context class now lazily loads all apsimNGpy
modules and classes that depend on the .NET runtime and the APSIM binary path.
This ensures that APSIM-specific objects are initialized and atatched to a specific APSIM version defined by the binary path, improving version control.
startup performance and runtime safety.
In addition, a new Apsim class has been introduced at the top-level
apsimNGpy module to enable dynamic management of different APSIM
versions across scripts or projects. This allows users to explicitly
control which APSIM binary is loaded at runtime.
Users may initialize the Apsim class in one of two modes:
Auto mode — No binary path is provided; the system resolves the APSIM installation automatically from the config.in path located in the user directory or from from global env.
Explicit mode — A binary path is supplied to load APSIM-dependent modules from a specific installation.
This design provides greater flexibility, version isolation, and improved runtime control when working with multiple APSIM installations.
Examples
Auto mode (automatic binary resolution):
from apsimNGpy import Apsim
apsim = Apsim()
Models = apsim.CLR.Models
Explicit mode (manual binary selection):
from apsimNGpy import Apsim
apsim = Apsim(bin_path="path/to/apsim/bin")
Models = apsim.CLR.Models
Using with context key word for scoped runtime control:
from apsimNGpy import Apsim
with Apsim("path/to/apsim/bin") as apsim:
with apsim.ApsimModel("Maize") as model:
model.run()
print(model.summarize_numeric())
use env path with a specified binary path key
from apsimNGpy import Apsim
with Apsim(dotenv_path = "path/to/apsim/bin.env",bin_key = '9780') as apsim:
with apsim.ApsimModel("Maize") as model:
model.run()
print(model.summarize_numeric())
Deprecations
The following methods are deprecated and will be removed in v1.6.0:
evaluate_simulated_output()Use
evaluate()instead.
preview_simulation()Use
open_in_gui()instead.
inspect_file()Use
tree()instead.