docs: public AnalyticModel getCalibratedModel(Set<ParameterObject> objectsToCalibrate) throws SolverException {
  		final ParameterAggregation<ParameterObject> parameterAggregate = new ParameterAggregation<>(objectsToCalibrate);
  
  		// Set solver parameters
  		final RandomVariable[] initialParameters;
  
  		// Apply parameter transformation to solver parameter space
  		if(parameterTransformation != null) {
  			initialParameters = parameterTransformation.getSolverParameter(parameterAggregate.getParameter());
  		} else {
  			initialParameters = parameterAggregate.getParameter();
  		}
  
  		final RandomVariable[] zeros				= new RandomVariable[calibrationProducts.size()];
  		final RandomVariable[] ones				= new RandomVariable[calibrationProducts.size()];
  		final RandomVariable[] lowerBound			= new RandomVariable[initialParameters.length];
  		final RandomVariable[] upperBound			= new RandomVariable[initialParameters.length];
  		java.util.Arrays.fill(zeros, new RandomVariableFromDoubleArray(0.0));
  		java.util.Arrays.fill(ones, new RandomVariableFromDoubleArray(1.0));
  		java.util.Arrays.fill(lowerBound, new RandomVariableFromDoubleArray(Double.NEGATIVE_INFINITY));
  		java.util.Arrays.fill(upperBound, new RandomVariableFromDoubleArray(Double.POSITIVE_INFINITY));
  
  		StochasticOptimizer.ObjectiveFunction objectiveFunction = new StochasticOptimizer.ObjectiveFunction() {
  			@Override
  			public void setValues(RandomVariable[] parameters, RandomVariable[] values) throws SolverException {
  				RandomVariable[] modelParameters = parameters;
  				try {
  					if(parameterTransformation != null) {
  						modelParameters = parameterTransformation.getParameter(parameters);
  						// Copy back the parameter constrain to inform the optimizer
  						System.arraycopy(parameterTransformation.getSolverParameter(modelParameters), 0, parameters, 0, parameters.length);
  					}
  
  					Map<ParameterObject, RandomVariable[]> curvesParameterPairs = parameterAggregate.getObjectsToModifyForParameter(modelParameters);
  					AnalyticModel modelClone = model.getCloneForParameter(curvesParameterPairs);
  					for(int i=0; i<calibrationProducts.size(); i++) {
  						values[i] = calibrationProducts.get(i).getValue(evaluationTime, modelClone);
  					}
  					if(calibrationTargetValues != null) {
  						for(int i=0; i<calibrationTargetValues.size(); i++) {
  							values[i].sub(calibrationTargetValues.get(i));
  						}
  					}
  				} catch (CloneNotSupportedException e) {
  					throw new SolverException(e);
  				}
  			}
  		};
  
  		if(optimizerFactory == null) {
  			int maxThreads		= Math.min(2 * Math.max(Runtime.getRuntime().availableProcessors(), 1), initialParameters.length);
  			optimizerFactory = new StochasticPathwiseOptimizerFactoryLevenbergMarquardt(maxIterations, calibrationAccuracy, maxThreads);
  		}
  
  		StochasticOptimizer optimizer = optimizerFactory.getOptimizer(objectiveFunction, initialParameters, lowerBound, upperBound, zeros);
  		optimizer.run();
  
  		iterations = optimizer.getIterations();
  
  		RandomVariable[] bestParameters = optimizer.getBestFitParameters();
  		if(parameterTransformation != null) {
  			bestParameters = parameterTransformation.getParameter(bestParameters);
  		}
  
  		AnalyticModel calibratedModel = null;
  		try {
  
  			Map<ParameterObject, RandomVariable[]> curvesParameterPairs = parameterAggregate.getObjectsToModifyForParameter(bestParameters);
  			calibratedModel = model.getCloneForParameter(curvesParameterPairs);
  		} catch (CloneNotSupportedException e) {
  			throw new SolverException(e);
  		}
  
  		accuracy = 0.0;
  		for(int i=0; i<calibrationProducts.size(); i++) {
  			double error = calibrationProducts.get(i).getValue(evaluationTime, calibratedModel).getStandardDeviation();
  			if(calibrationTargetValues != null) {
  				error -= calibrationTargetValues.get(i);
  			}
  			accuracy += error * error;
  		}
  		accuracy = Math.sqrt(accuracy/calibrationProducts.size());
  
  		return calibratedModel;
  	}