ExecutableModel* LLVMModelGenerator::createModel(const std::string& sbml,
        uint options)
{
    bool computeAndAssignConsevationLaws =
            options & ModelGenerator::ComputeAndAssignConsevationLaws;

    bool forceReCompile = options & ModelGenerator::ForceReCompile;

    string md5;

    if (!forceReCompile)
    {
        // check for a chached copy
        md5 = rr::getMD5(sbml);

        ModelPtrMap::const_iterator i;

        SharedModelPtr sp;

        cachedModelsMutex.lock();

        if ((i = cachedModels.find(md5)) != cachedModels.end())
        {
            sp = i->second.lock();
        }

        cachedModelsMutex.unlock();

        // we could have recieved a bad ptr, a model could have been deleted,
        // in which case, we should have a bad ptr.

        if (sp)
        {
            Log(Logger::PRIO_DEBUG) << "found a cached model for " << md5;
            return new LLVMExecutableModel(sp);
        }
        else
        {
            Log(Logger::PRIO_TRACE) << "no cached model found for " << md5
                    << ", creating new one";
        }
    }

    SharedModelPtr rc(new ModelResources());

    ModelGeneratorContext context(sbml, computeAndAssignConsevationLaws);

    rc->evalInitialConditionsPtr =
            EvalInitialConditionsCodeGen(context).createFunction();

    rc->evalReactionRatesPtr =
            EvalReactionRatesCodeGen(context).createFunction();

    rc->getBoundarySpeciesAmountPtr =
            GetBoundarySpeciesAmountCodeGen(context).createFunction();

    rc->getFloatingSpeciesAmountPtr =
            GetFloatingSpeciesAmountCodeGen(context).createFunction();

    rc->getBoundarySpeciesConcentrationPtr =
            GetBoundarySpeciesConcentrationCodeGen(context).createFunction();

    rc->getFloatingSpeciesConcentrationPtr =
            GetFloatingSpeciesConcentrationCodeGen(context).createFunction();

    rc->getCompartmentVolumePtr =
            GetCompartmentVolumeCodeGen(context).createFunction();

    rc->getGlobalParameterPtr =
            GetGlobalParameterCodeGen(context).createFunction();

    rc->evalRateRuleRatesPtr =
            EvalRateRuleRatesCodeGen(context).createFunction();

    rc->getEventTriggerPtr =
            GetEventTriggerCodeGen(context).createFunction();

    rc->getEventPriorityPtr =
            GetEventPriorityCodeGen(context).createFunction();

    rc->getEventDelayPtr =
            GetEventDelayCodeGen(context).createFunction();

    rc->eventTriggerPtr =
            EventTriggerCodeGen(context).createFunction();

    rc->eventAssignPtr =
            EventAssignCodeGen(context).createFunction();

    rc->evalVolatileStoichPtr =
            EvalVolatileStoichCodeGen(context).createFunction();

    rc->evalConversionFactorPtr =
            EvalConversionFactorCodeGen(context).createFunction();

    if (options & ModelGenerator::ReadOnlyModel)
    {
        rc->setBoundarySpeciesAmountPtr = 0;
        rc->setBoundarySpeciesConcentrationPtr = 0;
        rc->setFloatingSpeciesConcentrationPtr = 0;
        rc->setCompartmentVolumePtr = 0;
        rc->setFloatingSpeciesAmountPtr = 0;
        rc->setGlobalParameterPtr = 0;
    }
    else
    {
        rc->setBoundarySpeciesAmountPtr = SetBoundarySpeciesAmountCodeGen(
                context).createFunction();

        rc->setBoundarySpeciesConcentrationPtr =
                SetBoundarySpeciesConcentrationCodeGen(context).createFunction();

        rc->setFloatingSpeciesConcentrationPtr =
                SetFloatingSpeciesConcentrationCodeGen(context).createFunction();

        rc->setCompartmentVolumePtr =
                SetCompartmentVolumeCodeGen(context).createFunction();

        rc->setFloatingSpeciesAmountPtr = SetFloatingSpeciesAmountCodeGen(
                context).createFunction();

        rc->setGlobalParameterPtr =
                SetGlobalParameterCodeGen(context).createFunction();
    }


    // if anything up to this point throws an exception, thats OK, because
    // we have not allocated any memory yet that is not taken care of by
    // something else.
    // Now that everything that could have thrown would have thrown, we
    // can now create the model and set its fields.

    // * MOVE * the bits over from the context to the exe model.
    context.stealThePeach(&rc->symbols, &rc->context,
            &rc->executionEngine, &rc->errStr);

    if (!forceReCompile)
    {
        // check for a chached copy, another thread could have
        // created one while we were making ours...

        ModelPtrMap::const_iterator i;

        SharedModelPtr sp;

        cachedModelsMutex.lock();

        // whilst we have it locked, clear any expired ptrs
        for (ModelPtrMap::const_iterator j = cachedModels.begin();
                j != cachedModels.end();)
        {
            if (j->second.expired())
            {
                Log(Logger::PRIO_INFORMATION) <<
                        "removing expired model resource for hash " << md5;

                j = cachedModels.erase(j);
            }
            else
            {
                ++j;
            }
        }

        if ((i = cachedModels.find(md5)) == cachedModels.end())
        {
            Log(Logger::PRIO_INFORMATION) << "could not find existing cached resource "
                    "resources, for hash " << md5 <<
                    ", inserting new resources into cache";

            cachedModels[md5] = rc;
        }

        cachedModelsMutex.unlock();
    }

    return new LLVMExecutableModel(rc);
}
ExecutableModel* LLVMModelGenerator::createModel(const std::string& sbml,
        uint options)
{
    bool forceReCompile = options & LoadSBMLOptions::RECOMPILE;

    string md5;

    if (!forceReCompile)
    {
        // check for a chached copy
        md5 = rr::getMD5(sbml);

        if (options & LoadSBMLOptions::CONSERVED_MOIETIES)
        {
            md5 += "_conserved";
        }

        ModelPtrMap::const_iterator i;

        SharedModelPtr sp;

        cachedModelsMutex.lock();

        if ((i = cachedModels.find(md5)) != cachedModels.end())
        {
            sp = i->second.lock();
        }

        cachedModelsMutex.unlock();

        // we could have recieved a bad ptr, a model could have been deleted,
        // in which case, we should have a bad ptr.

        if (sp)
        {
            Log(Logger::LOG_DEBUG) << "found a cached model for " << md5;
            return new LLVMExecutableModel(sp, createModelData(*sp->symbols, sp->random));
        }
        else
        {
            Log(Logger::LOG_TRACE) << "no cached model found for " << md5
                    << ", creating new one";
        }
    }

    SharedModelPtr rc(new ModelResources());

    ModelGeneratorContext context(sbml, options);

    rc->evalInitialConditionsPtr =
            EvalInitialConditionsCodeGen(context).createFunction();

    rc->evalReactionRatesPtr =
            EvalReactionRatesCodeGen(context).createFunction();

    rc->getBoundarySpeciesAmountPtr =
            GetBoundarySpeciesAmountCodeGen(context).createFunction();

    rc->getFloatingSpeciesAmountPtr =
            GetFloatingSpeciesAmountCodeGen(context).createFunction();

    rc->getBoundarySpeciesConcentrationPtr =
            GetBoundarySpeciesConcentrationCodeGen(context).createFunction();

    rc->getFloatingSpeciesConcentrationPtr =
            GetFloatingSpeciesConcentrationCodeGen(context).createFunction();

    rc->getCompartmentVolumePtr =
            GetCompartmentVolumeCodeGen(context).createFunction();

    rc->getGlobalParameterPtr =
            GetGlobalParameterCodeGen(context).createFunction();

    rc->evalRateRuleRatesPtr =
            EvalRateRuleRatesCodeGen(context).createFunction();

    rc->getEventTriggerPtr =
            GetEventTriggerCodeGen(context).createFunction();

    rc->getEventPriorityPtr =
            GetEventPriorityCodeGen(context).createFunction();

    rc->getEventDelayPtr =
            GetEventDelayCodeGen(context).createFunction();

    rc->eventTriggerPtr =
            EventTriggerCodeGen(context).createFunction();

    rc->eventAssignPtr =
            EventAssignCodeGen(context).createFunction();

    rc->evalVolatileStoichPtr =
            EvalVolatileStoichCodeGen(context).createFunction();

    rc->evalConversionFactorPtr =
            EvalConversionFactorCodeGen(context).createFunction();

    if (options & LoadSBMLOptions::READ_ONLY)
    {
        rc->setBoundarySpeciesAmountPtr = 0;
        rc->setBoundarySpeciesConcentrationPtr = 0;
        rc->setFloatingSpeciesConcentrationPtr = 0;
        rc->setCompartmentVolumePtr = 0;
        rc->setFloatingSpeciesAmountPtr = 0;
        rc->setGlobalParameterPtr = 0;
    }
    else
    {
        rc->setBoundarySpeciesAmountPtr = SetBoundarySpeciesAmountCodeGen(
                context).createFunction();

        rc->setBoundarySpeciesConcentrationPtr =
                SetBoundarySpeciesConcentrationCodeGen(context).createFunction();

        rc->setFloatingSpeciesConcentrationPtr =
                SetFloatingSpeciesConcentrationCodeGen(context).createFunction();

        rc->setCompartmentVolumePtr =
                SetCompartmentVolumeCodeGen(context).createFunction();

        rc->setFloatingSpeciesAmountPtr = SetFloatingSpeciesAmountCodeGen(
                context).createFunction();

        rc->setGlobalParameterPtr =
                SetGlobalParameterCodeGen(context).createFunction();
    }

    if (options & LoadSBMLOptions::MUTABLE_INITIAL_CONDITIONS)
    {
        rc->getFloatingSpeciesInitConcentrationsPtr =
                GetFloatingSpeciesInitConcentrationCodeGen(context).createFunction();
        rc->setFloatingSpeciesInitConcentrationsPtr =
                SetFloatingSpeciesInitConcentrationCodeGen(context).createFunction();

        rc->getFloatingSpeciesInitAmountsPtr =
                GetFloatingSpeciesInitAmountCodeGen(context).createFunction();
        rc->setFloatingSpeciesInitAmountsPtr =
                SetFloatingSpeciesInitAmountCodeGen(context).createFunction();

        rc->getCompartmentInitVolumesPtr =
                GetCompartmentInitVolumeCodeGen(context).createFunction();
        rc->setCompartmentInitVolumesPtr =
                SetCompartmentInitVolumeCodeGen(context).createFunction();

        rc->getGlobalParameterInitValuePtr =
                GetGlobalParameterInitValueCodeGen(context).createFunction();
        rc->setGlobalParameterInitValuePtr =
                SetGlobalParameterInitValueCodeGen(context).createFunction();
    }
    else
    {
        rc->getFloatingSpeciesInitConcentrationsPtr = 0;
        rc->setFloatingSpeciesInitConcentrationsPtr = 0;

        rc->getFloatingSpeciesInitAmountsPtr = 0;
        rc->setFloatingSpeciesInitAmountsPtr = 0;

        rc->getCompartmentInitVolumesPtr = 0;
        rc->setCompartmentInitVolumesPtr = 0;

        rc->getGlobalParameterInitValuePtr = 0;
        rc->setGlobalParameterInitValuePtr = 0;
    }


    // if anything up to this point throws an exception, thats OK, because
    // we have not allocated any memory yet that is not taken care of by
    // something else.
    // Now that everything that could have thrown would have thrown, we
    // can now create the model and set its fields.

    LLVMModelData *modelData = createModelData(context.getModelDataSymbols(),
            context.getRandom());

    uint llvmsize = ModelDataIRBuilder::getModelDataSize(context.getModule(),
            &context.getExecutionEngine());

    if (llvmsize != modelData->size)
    {
        std::stringstream s;

        s << "LLVM Model Data size " << llvmsize << " is different from " <<
                "C++ size of LLVM ModelData, " << modelData->size;

        LLVMModelData_free(modelData);

        Log(Logger::LOG_FATAL) << s.str();

        throw_llvm_exception(s.str());
    }

    // * MOVE * the bits over from the context to the exe model.
    context.stealThePeach(&rc->symbols, &rc->context,
            &rc->executionEngine, &rc->random, &rc->errStr);


    if (!forceReCompile)
    {
        // check for a chached copy, another thread could have
        // created one while we were making ours...

        ModelPtrMap::const_iterator i;

        SharedModelPtr sp;

        cachedModelsMutex.lock();

        // whilst we have it locked, clear any expired ptrs
        for (ModelPtrMap::const_iterator j = cachedModels.begin();
                j != cachedModels.end();)
        {
            if (j->second.expired())
            {
                Log(Logger::LOG_DEBUG) <<
                        "removing expired model resource for hash " << md5;

                j = cachedModels.erase(j);
            }
            else
            {
                ++j;
            }
        }

        if ((i = cachedModels.find(md5)) == cachedModels.end())
        {
            Log(Logger::LOG_DEBUG) << "could not find existing cached resource "
                    "resources, for hash " << md5 <<
                    ", inserting new resources into cache";

            cachedModels[md5] = rc;
        }

        cachedModelsMutex.unlock();
    }

    return new LLVMExecutableModel(rc, modelData);
}