Advanced Topics
Topics covered in this chapter:
Iterative solving
It is possible to run the same compiled model multiple times with different inputs, for example to perform sensitivity analysis and scenario comparison. The package allows you to load a model once with load_model() and call run() repeatedly, each time supplying a different input_data dictionary. Each call solves a fresh instance of the problem and returns an independent output dictionary. Results across scenarios can be collected and analysed using standard Python data structures or Pandas.
Capacity scenario analysis
The following example tests four capacity levels (80%, 100%, 120%, and 150% of baseline) using the multi-period production model. Only the Capacity input varies between scenarios; all other inputs remain constant. The model is loaded once; run() automatically resets state between calls. Loading a model inside a loop is not recommended as it re-parses the BIM file on each iteration:
import moselpy as mp
import pandas as pd
import matplotlib.pyplot as plt
capacity_multipliers = [0.8, 1.0, 1.2, 1.5]
scenario_profits = {}
model = mp.load_model("production.bim")
model.set_default_stream(mp.StreamType.OUTPUT, "null:")
model.set_default_stream(mp.StreamType.ERROR, "null:")
for mult in capacity_multipliers:
output = model.run(
exec_params={"DATASOURCE": "moselpy:"},
input_data={
"Profit": profit_series,
"Demand": demand_df,
"Capacity": capacity_series * mult,
"Duration": duration_series,
})
scenario_profits[f"{mult:.0%}"] = output["TotalProfit"]
# Build a Series for easy analysis
results = pd.Series(scenario_profits, name="Total Profit")
print(results.apply(lambda x: f"${x:,.0f}")) Output:
80% $27,982 100% $34,808 120% $41,633 150% $51,325
Plotting scenario results
Visualize how profit scales with capacity. The pd.Series built above provides a ready-made source for a bar chart:
fig, ax = plt.subplots(figsize=(8, 5))
bars = ax.bar(results.index, results.values, edgecolor="black")
for bar, val in zip(bars, results.values):
ax.text(bar.get_x() + bar.get_width() / 2, bar.get_height() + 500,
f"${val:,.0f}", ha="center", va="bottom", fontweight="bold")
ax.set_title("Profit vs Capacity Level")
ax.set_xlabel("Capacity (% of baseline)")
ax.set_ylabel("Total Profit ($)")
ax.set_ylim(0, max(results.values) * 1.15)
plt.tight_layout()
plt.show() 
Figure 5.1: Profit vs capacity level for four capacity scenarios
Model control
The package provides methods to control running models from Python. This is useful for implementing timeouts, user-initiated cancellation, or coordinating multiple model runs.
Graceful stop
Use model.stop() to request the model to stop at the next control flow point (loop, conditional, procedure call). The Mosel runtime checks this flag automatically:
import moselpy as mp
model = mp.load_model("long_running.bim")
# From a callback or timer thread
if model.is_running:
stopped = model.stop() # Returns True if stop was requested
print(f"Stop requested: {stopped}")
# After execution
if model.exec_status == mp.ExecStatus.STOP:
print("Model was stopped by user") Immediate termination
Use model.terminate() for immediate termination without waiting for a safe stopping point. Use with caution as it may leave model state inconsistent:
model.terminate() # Immediate termination
Model introspection
The MoselPy package allows you to inspect compiled model metadata directly from Python without running the model. Properties such as name, version, size, and user_comment are available on any loaded model object. It is also possible to retrieve custom metadata embedded in the Mosel source via get_annotations(), and to query encryption and signing status through the sec_status object. This is useful for validation, logging, and build pipelines that need to verify model provenance:
import moselpy as mp
model = mp.load_model("model.bim")
print(f"Model name: {model.name}")
print(f"BIM version: {model.version}")
print(f"File size: {model.size} bytes")
print(f"User comment: {model.user_comment}")
# Security status
if model.sec_status.is_encrypted:
print("Model is encrypted")
if model.sec_status.is_signed:
print(f"Signed with key: {model.key_fingerprint}") Output:
Model name: multiperiod_production BIM version: 0 File size: 18888 bytes User comment:
Annotations
Annotations are custom metadata defined in Mosel source using !@ directives. Any key-value pair can be defined and will be available via get_annotations() at runtime. Annotations are organized in dot-separated categories (e.g., doc.descr, insight.alias).
model "annotated"
!@description: Production planning model
!@author: Team Name
!@version: 1.2
!@mc.flush ! Not associated with declarations block
declarations
!@doc.descr The integer variable
public n: integer
end-declarations
n := 42
end-model Read annotations from Python (compile with the D flag to record doc-category annotations):
mp.compile_model("model.mos", "model.bim", "D")
model = mp.load_model("model.bim")
# Model-level annotations
ann = model.get_annotations()
for key, value in ann.items():
print(f" {key}: {value}")
# Annotations for a specific public identifier
n_ann = model.get_annotations(ident="n")
print(n_ann.get("doc.descr")) # "The integer variable"
# Filter by prefix
doc_ann = model.get_annotations(prefix="doc") Output:
.author: Team Name .description: Production planning model .version: 1.2 doc.name: annotated doc.version: 0.0.0 The decision variable
Use !@mc.flush to indicate that the preceding annotations are not meant to be associated with the following block (declarations, subroutine definitions, etc.).
Context managers
The package allows you to use Python's with statement to manage model resources automatically. When the with block exits (whether normally or due to an exception), the model is released and any associated resources are cleaned up. This is the recommended pattern for short-lived model executions where you do not need to retain the model object afterwards:
import moselpy as mp
with mp.load_model("model.bim") as model:
output = model.run(input_data=data)
print(output['result'])
# Model resources automatically released on exit Performance considerations
Binary format for large data
It is possible to significantly reduce data transfer overhead by using binary format for large datasets. The package allows you to serialize Python data to a compact binary representation before passing it to Mosel. For datasets with thousands of elements, this can be measurably faster than the default text-based exchange.
For file-based transfer, use write_binary() to serialize to a file and read_binary() to deserialize. The binary file can be loaded directly in Mosel using the bin: driver:
import moselpy as mp
data = {"values": {1: 10, 2: 20, 3: 30}, "multiplier": 2.5}
mp.write_binary("data.bin", data)
# In Mosel:
# initializations from "bin:data.bin"
# values multiplier
# end-initializations
loaded = mp.read_binary("data.bin") For in-memory transfer without files, use to_binary() to serialize data to bytes and pass the bytes object directly in input_data. Use from_binary() to deserialize output bytes:
import moselpy as mp
large_data = {"values": {i: random.random() for i in range(100000)}}
bin_data = mp.to_binary(large_data)
output = model.run(input_data={"data": bin_data})
result_data = mp.from_binary(output["output_bytes"]) Binary format is compatible with Mosel's bin: I/O driver; use "bin:moselpy:mydata" for accessing the data from Mosel. See Chapter I/O Driver for more on the I/O driver and Chapter Binary Format Utilities for the full binary format API.
Exporting problems
The model's optimization problem can be exported to LP or MPS format for debugging, external analysis, or solver comparison:
model = mp.load_model("model.bim")
model.run(input_data=data)
# Export to LP format (default)
model.export_problem(file_name="problem.lp")
# Export to MPS format with maximization
model.export_problem(options="mp", file_name="problem.mps")
# Export to console (no file_name)
model.export_problem() See Chapter Model Class for the full export_problem() reference including all format options.
© 2001-2026 Fair Isaac Corporation. All rights reserved. This documentation is the property of Fair Isaac Corporation (“FICO”). Receipt or possession of this documentation does not convey rights to disclose, reproduce, make derivative works, use, or allow others to use it except solely for internal evaluation purposes to determine whether to purchase a license to the software described in this documentation, or as otherwise set forth in a written software license agreement between you and FICO (or a FICO affiliate). Use of this documentation and the software described in it must conform strictly to the foregoing permitted uses, and no other use is permitted.
