@set:KSLControl annotation and access them with model.controls()RVParameterSetter classModelBuilderIfc interfaceScenario, ScenarioRunner, and ConcurrentScenarioRunnerMultipleComparisonAnalyzer (MCB)@set:KSLControl and model.controls()RVParameterSetterModelBuilderIfc, a repeatable model recipeScenario with ScenarioRunner (sequential) or ConcurrentScenarioRunner (parallel)MultipleComparisonAnalyzer (MCB)PalletWorkCenter model, varying the number of workers@set:KSLControl Annotationksl.controls package)@set:KSLControl; supply a controlType from the ControlType enum plus optional boundsnumWorkers on PalletWorkCenter is exposed as an integer control with a lower bound of 1Model.controls() returns all controls for every element in the modelPWC.numWorkersvalue; the underlying property updatesVariable has an initialValue control)val model = Model("Pallet Model MCB")
val palletWorkCenter = PalletWorkCenter(model, name = "PWC")
val controls = model.controls()
for (controlKey in controls.controlKeys()) {
println(controlKey)
}
// look up a control by its unique key, then set it
val control = controls.control("PWC.numWorkers")!!
control.value = 3.0 // numWorkers is now 3PWC.numWorkers
NumBusyWorkers.initialValue
Num Processed.initialCounterLimit
P{total time > 480 minutes}.initialValue
RVParameterSetter (ksl.rvariable.parameters)rvParameters maps each RV’s name to an RVParameters object holding its Double/Int/DoubleArray parameterschangeDoubleParameter(name, value) — but the change is staged, not yet appliedapplyParameterChanges(model) to push the change into the modelval setter = RVParameterSetter(model)
val map = setter.rvParameters
for ((key, value) in map) {
println("$key -> $value")
}
// change the mode of the triangular processing-time RV
val rv = map["ProcessingTimeRV"]!!
rv.changeDoubleParameter("mode", 14.0)
setter.applyParameterChanges(model) // <-- required to take effectRVParameterSetter also exposes a flattened view via flatParametersAsDoubles. + parameter name, so every parameter gets a unique string keyPWC.numWorkers) — one uniform naming scheme for both controls and RV parametersProcessingTimeRV.min -> 8.0
ProcessingTimeRV.mode -> 14.0
ProcessingTimeRV.max -> 15.0
TransportTimeRV.mean -> 5.0
NumPalletsRV.probOfSuccess -> 0.8
NumPalletsRV.numTrials -> 100.0
ModelBuilderIfcModelBuilderIfcbuild() returns a fresh, fully-configured Model; calling it twice yields two completely independent modelsbuild() simple and set run defaults (replications, run length) inside itinterface ModelBuilderIfc {
fun build(
modelConfiguration: Map<String, String>? = null,
experimentRunParameters: ExperimentRunParametersIfc? = null
): Model
}
object BuildPalletWorkCenter : ModelBuilderIfc {
override fun build(
modelConfiguration: Map<String, String>?,
experimentRunParameters: ExperimentRunParametersIfc?
): Model {
val model = Model("Pallet Model")
PalletWorkCenter(model, name = "PWC")
model.numberOfReplications = 30
return model
}
}ScenarioRunnerScenario = a model (or model builder) + an inputs map of flat key → value + a unique nameScenarioRunner batch-runs a list of scenarios and captures every result in a KSLDatabase (kslDb)fun buildScenarios(): List<Scenario> {
val model = Model("Pallet Model")
PalletWorkCenter(model, name = "PWC")
model.numberOfReplications = 30
val s1 = Scenario(model = model, name = "One Worker",
inputs = mapOf("PWC.numWorkers" to 1.0))
val s2 = Scenario(model = model, name = "Two Worker",
inputs = mapOf("PWC.numWorkers" to 2.0))
val s3 = Scenario(model = model, name = "Three Worker",
inputs = mapOf("PWC.numWorkers" to 3.0))
return listOf(s1, s2, s3)
}
val runner = ScenarioRunner("Pallet Comparison", buildScenarios())
runner.simulate()ConcurrentScenarioRunnerScenarioRunner runs scenarios one at a time; ConcurrentScenarioRunner runs them in parallel, cutting wall-clock time by roughly the number of coressimulate() is a suspend function, so wrap main in runBlockingModel: each scenario calls build() for its own private model — a shared model would race. Submitting a shared-model scenario throws immediately.useIndependentRandomStreams() to opt outfun main() = runBlocking {
val s1 = Scenario(BuildPalletWorkCenter, "One Worker", mapOf("PWC.numWorkers" to 1.0))
val s2 = Scenario(BuildPalletWorkCenter, "Two Worker", mapOf("PWC.numWorkers" to 2.0))
val s3 = Scenario(BuildPalletWorkCenter, "Three Worker", mapOf("PWC.numWorkers" to 3.0))
val runner = ConcurrentScenarioRunner("Pallet Comparison", listOf(s1, s2, s3))
runner.simulate()
runner.print()
}KSLDatabase (runner.kslDb), organized by scenario (see output structure)MultipleComparisonAnalyzer (MCB) directly from the database to screen for the best configuration — same API for either runnermultipleComparisonAnalyzerFor() takes the experiment names and the response name to compare