Exemple #1
0
class ChemostatDDE2_ESA(NumericalModel):
    label = "DDE Chemostat (Example 2 - ESA)"
    description = F.ModelDescription(
        "Chemostat model with delay differential equations (DDE) and extremum seeking algorithm (ESA) - Example 2",
        show=True)
    figure = F.ModelFigure(
        src="BioReactors/img/ModuleImages/SimpleChemostat.png", show=False)

    #1. ############ Inputs ###############
    #1.1 Fields - Input values
    # Parameters
    k1 = F.Quantity(
        default=0,
        minValue=0,
        maxValue=1e6,
        label='k<sub>1</sub>',
        description=
        'yield coefficient related to substrate-1 consumption from bacteria-1')
    k2 = F.Quantity(
        default=0,
        minValue=0,
        maxValue=1e6,
        label='k<sub>2</sub>',
        description=
        'yield coefficient related to substrate-2 production from bacteria-1')
    k3 = F.Quantity(
        default=0,
        minValue=0,
        maxValue=1e6,
        label='k<sub>3</sub>',
        description=
        'yield coefficient related to substrate-2 consumption from bacteria-2')
    k4 = F.Quantity(
        default=0,
        minValue=0,
        maxValue=1e6,
        label='k<sub>4</sub>',
        description=
        'yield coefficient of methane (biogas) on bacteria-2 and substrate-2')
    s1_in = F.Quantity('Bio_MassConcentration',
                       default=(0, 'g/L'),
                       label='s<sub>1</sub><sup>in</sub>',
                       description='input substrate-1 concentration')
    s2_in = F.Quantity('Bio_MassConcentration',
                       default=(0, 'g/L'),
                       label='s<sub>2</sub><sup>in</sub>',
                       description='input substrate-2 concentration')
    a = F.Quantity(
        'Fraction',
        default=(0, '-'),
        label='&#945',
        description=
        'proportion of organisms that are affected by the dilution rate D')
    D = F.Quantity('Bio_TimeRate',
                   default=(0, '1/day'),
                   minValue=(0, '1/day'),
                   label='D',
                   description='dilution rate')
    parametersFG = F.FieldGroup([s1_in, s2_in, k1, k2, k3, k4, a, D],
                                label='Parameters')

    # Parameters - specific growth rates
    m1 = F.Quantity('Bio_TimeRate',
                    default=(0, '1/day'),
                    minValue=(0, '1/day'),
                    label='m<sub>1</sub>',
                    description='maximum specific growth rate of bacteria-1')
    m2 = F.Quantity('Bio_TimeRate',
                    default=(0, '1/day'),
                    minValue=(0, '1/day'),
                    label='m<sub>2</sub>',
                    description='maximum specific growth rate of bacteria-2')
    k_s1 = F.Quantity('Bio_MassConcentration',
                      default=(0, 'g/L'),
                      label='k<sub>s1</sub>',
                      description='half saturation constant of bacteria-1')
    k_s2 = F.Quantity('Bio_MassConcentration',
                      default=(0, 'g/L'),
                      label='k<sub>s2</sub>',
                      description='half saturation constant of bacteria-2')
    k_I = F.Quantity('Dimensionless',
                     default=(0, '-'),
                     label='k<sub>I</sub>',
                     description='constant in unit: [ sqrt( g/L ) ]')
    parametersMuFG = F.FieldGroup([m1, m2, k_s1, k_s2, k_I],
                                  label='Parameters - specific growth rates')

    # Parameters - time delays
    tau1 = F.Quantity(
        'Bio_Time',
        default=(0, 'day'),
        minValue=(0, 'day'),
        label='&#964<sub>1</sub>',
        description=
        'time delay in conversion of the substrate-1 to viable biomass for bacteria-1'
    )
    tau2 = F.Quantity(
        'Bio_Time',
        default=(0, 'day'),
        minValue=(0, 'day'),
        label='&#964<sub>2</sub>',
        description=
        'time delay in conversion of the substrate-2 to viable biomass for bacteria-2'
    )
    parametersTauFG = F.FieldGroup([tau1, tau2],
                                   label='Parameters - time delay')

    # historical initial conditions
    s1_hist_vals = F.Quantity(
        'Bio_MassConcentration',
        default=(0, 'g/L'),
        label='s<sub>1</sub><sup>&#91-&#964<sub>1</sub>, 0&#93</sup>',
        description='historical initial condition for substrate-1 concentration'
    )
    x1_hist_vals = F.Quantity(
        'Bio_MassConcentration',
        default=(0, 'g/L'),
        label='x<sub>1</sub><sup>&#91-&#964<sub>1</sub>, 0&#93</sup>',
        description='historical initial condition for bacteria-1 concentration'
    )
    s2_hist_vals = F.Quantity(
        'Bio_MassConcentration',
        default=(0, 'g/L'),
        label='s<sub>2</sub><sup>&#91-&#964<sub>2</sub>, 0&#93</sup>',
        description='historical initial condition for substrate-2 concentration'
    )
    x2_hist_vals = F.Quantity(
        'Bio_MassConcentration',
        default=(0, 'g/L'),
        label='x<sub>2</sub><sup>&#91-&#964<sub>2</sub>, 0&#93</sup>',
        description='historical initial condition for bacteria-2 concentration'
    )
    parametersHistFG = F.FieldGroup(
        [s1_hist_vals, x1_hist_vals, s2_hist_vals, x2_hist_vals],
        label='Historical initial conditions')

    inputValuesSG = F.SuperGroup(
        [parametersMuFG, parametersFG, parametersTauFG, parametersHistFG],
        label="Input values")

    #1.2 Fields - Settings
    ESA_enable = F.Boolean(
        default=False,
        label='enable',
        description=
        'find the the maximum methane flow rate (i.e. run extremum seeking algorithm)'
    )
    ESA_DMin = F.Quantity('Bio_TimeRate',
                          default=(0, '1/day'),
                          minValue=(0.0, '1/day'),
                          label='D<sub>min</sub>',
                          description='minimum dilution rate',
                          show='self.ESA_enable')
    ESA_DMax = F.Quantity('Bio_TimeRate',
                          default=(0, '1/day'),
                          minValue=(0.0, '1/day'),
                          label='D<sub>max</sub>',
                          description='maximum dilution rate',
                          show='self.ESA_enable')
    ESA_eps = F.Quantity('Bio_TimeRate',
                         default=(0, '1/day'),
                         minValue=(1e-9, '1/day'),
                         maxValue=(1.0, '1/day'),
                         label='&#949*',
                         description='D max tolerance',
                         show='self.ESA_enable')
    ESA_h = F.Quantity('Bio_TimeRate',
                       default=(0, '1/day'),
                       minValue=(1e-8, '1/day'),
                       maxValue=(1.0, '1/day'),
                       label='h*',
                       description='seeking step of D max',
                       show='self.ESA_enable')
    ESA_eps_z = F.Quantity('Bio_MassConcentration',
                           default=(0, 'g/L'),
                           minValue=(1e-9, 'g/L'),
                           maxValue=(1.0, 'g/L'),
                           label='&#949<sub>z</sub>*',
                           description='equilibrium point tolerance',
                           show='self.ESA_enable')
    ESA_FG = F.FieldGroup(
        [ESA_enable, ESA_DMin, ESA_DMax, ESA_eps, ESA_h, ESA_eps_z],
        label='Extremum Seeking Algorithm (ESA)')

    DsQs_DMin = F.Quantity('Bio_TimeRate',
                           default=(0, '1/day'),
                           minValue=(0, '1/day'),
                           label='D<sub>min</sub>',
                           description='minimum dilution rate')
    DsQs_DMax = F.Quantity('Bio_TimeRate',
                           default=(0, '1/day'),
                           minValue=(0, '1/day'),
                           label='D<sub>max</sub>',
                           description='maximum dilution rate')
    DsQs_Step = F.Quantity('Bio_TimeRate',
                           default=(0, '1/day'),
                           minValue=(1e-4, '1/day'),
                           maxValue=(1.0, '1/day'),
                           label='D<sub>step</sub>',
                           description='step of dilution rate')
    DsQs_FG = F.FieldGroup([DsQs_DMin, DsQs_DMax, DsQs_Step],
                           label='Chart (Ds, Qs)')

    tFinal = F.Quantity('Bio_Time',
                        default=(100, 'day'),
                        minValue=(0, 'day'),
                        maxValue=(5000, 'day'),
                        label='simulation time')
    tPrint = F.Quantity('Bio_Time',
                        default=(0.1, 'day'),
                        minValue=(1e-5, 'day'),
                        maxValue=(100, 'day'),
                        label='print interval*')
    mainSimStep = F.Quantity('Bio_Time',
                             default=(10., 'day'),
                             minValue=(0.25, 'day'),
                             maxValue=(1e3, 'day'),
                             label='main simulation step*')
    absTol = F.Quantity('Bio_Time',
                        default=(1e-12, 'day'),
                        minValue=(1e-16, 'day'),
                        maxValue=(1e-5, 'day'),
                        label='absolute tolerance*')
    relTol = F.Quantity('Bio_Time',
                        default=(1e-12, 'day'),
                        minValue=(1e-16, 'day'),
                        maxValue=(1e-3, 'day'),
                        label='relative tolerance*')
    solverFG = F.FieldGroup([tFinal, tPrint, mainSimStep, absTol, relTol],
                            label='Solver')

    settingsSG = F.SuperGroup([solverFG, DsQs_FG, ESA_FG], label='Settings')

    #1.4 Model view
    inputView = F.ModelView(ioType="input",
                            superGroups=[inputValuesSG, settingsSG],
                            autoFetch=True)

    #2. ############ Results ###############
    # 2.1. Results
    dataSeries = (
        ('time', F.Quantity('Bio_Time', default=(1, 'day'))),
        ('s1', F.Quantity('Bio_MassConcentration', default=(1, 'g/L'))),
        ('x1', F.Quantity('Bio_MassConcentration', default=(1, 'g/L'))),
        ('s2', F.Quantity('Bio_MassConcentration', default=(1, 'g/L'))),
        ('x2', F.Quantity('Bio_MassConcentration', default=(1, 'g/L'))),
        ('D', F.Quantity('Bio_TimeRate', default=(1, '1/day'))),
        ('Q',
         F.Quantity('Bio_MassConcentrationFlowRate', default=(1, 'g/L/day'))),
    )

    plot = F.PlotView(
        dataSeries,
        label='Plot',
        options={
            'ylabel': None,
            'title': 'Chemostat (DDE)'
        },
    )
    table = F.TableView(dataSeries,
                        label='Table',
                        options={
                            'title':
                            'Title',
                            'formats': [
                                '0.0000', '0.0000', '0.0000', '0.0000',
                                '0.0000', '0.0000', '0.0000'
                            ]
                        })

    chartS1S2 = F.MPLPlot(label='Chart (s<sub>1</sub>, s<sub>2</sub>)')
    chartX1X2 = F.MPLPlot(label='Chart (x<sub>1</sub>, x<sub>2</sub>)')
    chartS1X1 = F.MPLPlot(label='Chart (s<sub>1</sub>, x<sub>1</sub>)')
    chartS2X2 = F.MPLPlot(label='Chart (s<sub>2</sub>, x<sub>2</sub>)')
    chartS2X2 = F.MPLPlot(label='Chart (s<sub>2</sub>, x<sub>2</sub>)')
    chartDQ = F.MPLPlot(label='Chart (D, Q)')
    chartDsQs = F.MPLPlot(label='Chart (Ds, Qs)')

    resultsVG = F.ViewGroup([
        plot, table, chartS1S2, chartX1X2, chartS1X1, chartS2X2, chartDQ,
        chartDsQs
    ],
                            label='Results')
    resultsSG = F.SuperGroup([resultsVG], label="Results")

    # 2.2 Equilibrium point
    s1_eqpnt = F.Quantity(
        'Bio_MassConcentration',
        default=(0., 'g/L'),
        label='s<sub>1</sub><sup>*</sup>',
        description='substrate-1 concentration at the equilibrium point')
    x1_eqpnt = F.Quantity(
        'Bio_MassConcentration',
        default=(0., 'g/L'),
        label='x<sub>1</sub><sup>*</sup>',
        description='bacteria-1 concentration at the equilibrium point')
    s2_eqpnt = F.Quantity(
        'Bio_MassConcentration',
        default=(0., 'g/L'),
        label='s<sub>2</sub><sup>*</sup>',
        description='substrate-2 concentration at the equilibrium point')
    x2_eqpnt = F.Quantity(
        'Bio_MassConcentration',
        default=(0., 'g/L'),
        label='x<sub>2</sub><sup>*</sup>',
        description='bacteria-1 concentration at the equilibrium point')

    equilibriumPointFG = F.FieldGroup([s1_eqpnt, x1_eqpnt, s2_eqpnt, x2_eqpnt],
                                      label='Equilibrium point')
    equilibriumPointSG = F.SuperGroup([equilibriumPointFG],
                                      label='Equilibrium point')

    # 2.3 ESA Results
    resESA_QMaxIsFound = F.Boolean(label='Q<sub>max</sub> is found')
    resESA_DMax = F.Quantity('Bio_TimeRate',
                             default=(0, '1/day'),
                             label='D<sub>max</sub>',
                             description='maximum dilution rate')
    resESA_QMax = F.Quantity('Bio_MassConcentrationFlowRate',
                             default=(0, 'g/L/day'),
                             label='Q<sub>max</sub>',
                             description='maximum methane (biogas) flow rate')
    resESA_FG = F.FieldGroup([resESA_QMaxIsFound, resESA_DMax, resESA_QMax],
                             label='Results (ESA)')
    resESA_SG = F.SuperGroup([resESA_FG], label='Results (ESA)')

    #2.1 Model view
    resultView = F.ModelView(
        ioType="output",
        superGroups=[resultsSG, resESA_SG, equilibriumPointSG])

    ############# Page structure ########
    modelBlocks = [inputView, resultView]

    def __init__(self):
        self.k1 = (10.53, '-')
        self.k2 = (28.6, '-')
        self.k3 = (1074, '-')
        self.k4 = (100, '-')
        self.s1_in = (7.5, 'g/L')
        self.s2_in = (75, 'g/L')
        self.a = (0.5, '-')
        self.m1 = (1.2, '1/day')
        self.m2 = (0.74, '1/day')
        self.k_s1 = (7.1, 'g/L')
        self.k_s2 = (9.28, 'g/L')
        self.k_I = (16, '-')
        self.D = (0.25, '1/day')
        self.tau1 = (2, 'day')
        self.tau2 = (7, 'day')
        self.s1_hist_vals = (2, 'g/L')
        self.x1_hist_vals = (0.1, 'g/L')
        self.s2_hist_vals = (10, 'g/L')
        self.x2_hist_vals = (0.05, 'g/L')

        self.DsQs_DMin = (0.0, '1/day')
        self.DsQs_DMax = (1.0, '1/day')
        self.DsQs_Step = (0.01, '1/day')

        self.tFinal = (2000, 'day')
        self.tPrint = (1.0, 'day')
        self.mainSimStep = (10., 'day')
        self.absTol = (1e-12, 'day')
        self.relTol = (1e-12, 'day')

        self.ESA_enable = True
        self.ESA_DMin = (0.01, '1/day')
        self.ESA_DMax = (0.33, '1/day')
        self.ESA_eps = (0.01, '1/day')
        self.ESA_h = (0.025, '1/day')
        self.ESA_eps_z = (0.01, 'g/L')

    def compute(self):
        chemostatDDE = DM.ChemostatDDE2_ESA(self)
        if self.ESA_enable:
            chemostatDDE.runESA(self)
        else:
            chemostatDDE.run(self)

        resESA = chemostatDDE.getResultsESA()
        self.resESA_QMaxIsFound = resESA['QMaxIsFound']
        self.resESA_DMax = (resESA['DMax'], '1/day')
        self.resESA_QMax = (resESA['QMax'], 'kg/m**3/day')

        res = chemostatDDE.getResults()
        results = np.array([
            res['t'], res['s1'], res['x1'], res['s2'], res['x2'], res['D'],
            res['Q']
        ]).transpose()
        self.plot = results
        self.table = results

        chemostatDDE.plotS1S2(self.chartS1S2)
        chemostatDDE.plotX1X2(self.chartX1X2)
        chemostatDDE.plotS1X1(self.chartS1X1)
        chemostatDDE.plotS2X2(self.chartS2X2)
        chemostatDDE.plotDQ(self.chartDQ)
        chemostatDDE.plotDsQs(self.chartDsQs)

        self.s1_eqpnt = (chemostatDDE.equilibriumPoint[0], 'kg/m**3')
        self.x1_eqpnt = (chemostatDDE.equilibriumPoint[1], 'kg/m**3')
        self.s2_eqpnt = (chemostatDDE.equilibriumPoint[2], 'kg/m**3')
        self.x2_eqpnt = (chemostatDDE.equilibriumPoint[3], 'kg/m**3')
Exemple #2
0
class BiochemicalReactions(NumericalModel):
    label = "Biochemical reactions"
    description = F.ModelDescription(
        "Solver for elementary biochemical reactions", show=True)
    figure = F.ModelFigure(
        src="BioReactors/img/ModuleImages/BiochemicalReactions.png",
        show=False)

    async = True
    progressOptions = {'suffix': 's', 'fractionOutput': True}

    #1. ############ Inputs ###############
    #1.1 Fields - Input values
    reactions = F.RecordArray(
        (
            ('reactionEquation',
             F.String('E + S <-> ES', label='Equation', inputBoxWidth=160)),
            ('rateConstnats',
             F.String('0.0, 0.0', label='Rate constants', inputBoxWidth=100)),
            ('reactionName',
             F.String('enzyme binding to a substrate',
                      label='Name',
                      inputBoxWidth=400,
                      showTooltip=True)),
        ),
        toggle=False,
        label='Reactions',
        numRows=2,
        description='Biochemical reactions',
    )
    reactionsFG = F.FieldGroup([reactions],
                               hideContainer=True,
                               label="Reactions")
    reactionsSG = F.SuperGroup([reactionsFG], label="Reactions")

    species = F.RecordArray(
        (
            ('speciesVariable',
             F.String('E', label='Variable', inputBoxWidth=70)),
            ('initialValue',
             F.Quantity('Bio_MolarConcentration',
                        default=(1, 'M'),
                        minValue=(0, 'M'),
                        label='Initial value',
                        inputBoxWidth=75)),
            ('speciesName', F.String('enzyme', label='Name',
                                     inputBoxWidth=270)),
        ),
        toggle=False,
        label='species',
        numRows=4,
        description='Species of the reactions',
    )
    speciesFG = F.FieldGroup([species], hideContainer=True, label="Species")
    speciesSG = F.SuperGroup([speciesFG], label="Species")

    #1.2 Fields - Settings
    tFinal = F.Quantity('Time',
                        default=(0.0, 's'),
                        minValue=(0, 's'),
                        maxValue=(1000, 's'),
                        label='simulation time')
    tPrint = F.Quantity('Time',
                        default=(0.0, 's'),
                        minValue=(1e-3, 's'),
                        maxValue=(100, 's'),
                        label='print interval')
    solverFG = F.FieldGroup([tFinal, tPrint], label='Solver')

    settingsSG = F.SuperGroup([solverFG], label='Settings')

    #1.4 Model view
    exampleAction = A.ServerAction(
        "loadEg",
        label="Examples",
        options=(
            ('exampleMMK', 'Michaelis-Menten kinetics'),
            ('exampleMMKI', 'Michaelis-Menten kinetics with inhibition'),
        ))

    inputView = F.ModelView(
        ioType="input",
        superGroups=[reactionsSG, speciesSG, settingsSG],
        autoFetch=True,
        actionBar=A.ActionBar([exampleAction]),
    )

    #2. ############ Results ###############
    storage = F.HdfStorage(hdfFile=DM.dataStorageFilePath,
                           hdfGroup=DM.dataStorageDatasetPath)

    dataSeries = (
        ('time', F.Quantity('Time', default=(1, 's'))),
        ('E', F.Quantity('Bio_MolarConcentration', default=(1, 'M'))),
    )

    plot = F.PlotView(dataSeries,
                      label='Plot',
                      useHdfStorage=True,
                      storage='storage')

    table = F.TableView(
        dataSeries,
        label='Table',
        options={'formats': ['0.000', '0.000', '0.000', '0.000']},
        useHdfStorage=True,
        storage='storage')

    storageVG = F.ViewGroup([storage], show="false")
    resultsVG = F.ViewGroup([plot, table], label='Results')
    resultsSG = F.SuperGroup([resultsVG, storageVG], label='Results')

    # 2.2 ODEs plot
    odesPlot = F.MPLPlot(label='ODEs')
    odesVG = F.ViewGroup([odesPlot], label='Ordinary differential equations')
    odesSG = F.SuperGroup([odesVG], label='ODEs')

    # 2.3 Results plot
    chartPlot = F.MPLPlot(label='Chart')
    chartPlotVG = F.ViewGroup([chartPlot], label='Chart')
    chartPlotSG = F.SuperGroup([chartPlotVG], label='Chart')

    #2.1 Model view
    resultView = F.ModelView(ioType="output",
                             superGroups=[resultsSG, odesSG, chartPlotSG],
                             keepDefaultDefs=True)

    ############# Page structure ########
    modelBlocks = [inputView, resultView]

    def __init__(self):
        self.exampleMMK()

    def exampleMMK(self):
        self.species[0] = ('E', 4.0, 'enzyme')
        self.species[1] = ('S', 8.0, 'substrate')
        self.species[2] = ('ES', 0.0, 'complex')
        self.species[3] = ('P', 0.0, 'product')

        self.reactions[0] = (
            'E + S <=> ES', '2.0, 1.0',
            'an enzyme binding to a substrate form an enzyme-substrate complex (reversible process)'
        )
        self.reactions[1] = (
            'ES -> E + P', '1.5',
            'an enzyme-substrate complex decomposition to a product and the enzyme'
        )

        self.tFinal = 20.0
        self.tPrint = 0.01

    def exampleMMKI(self):
        self.species.resize(6)
        self.species[0] = ('E', 4.0, 'enzyme')
        self.species[1] = ('S', 8.0, 'substrate')
        self.species[2] = ('ES', 0.0, 'enzyme-substrate complex')
        self.species[3] = ('P', 0.0, 'product')
        self.species[4] = ('I', 5.0, 'inhibitor')
        self.species[5] = ('EI', 0.0, 'inactive enzyme-inhibitor complex')

        self.reactions.resize(3)
        self.reactions[0] = (
            'E + S <=> ES', '2.0, 1.0',
            'an enzyme binding to a substrate form an enzyme-substrate complex (reversible process)'
        )
        self.reactions[1] = (
            'ES -> E + P', '1.5',
            'an enzyme-substrate complex decomposition to a product and an enzyme'
        )
        self.reactions[2] = (
            'E + I <=> EI', '2.0, 0.5',
            'an inhibitor binding to the active site of an enzyme form an inactive enzyme-inhibitor complex (reversible processes)'
        )

        self.tFinal = 20.0
        self.tPrint = 0.01

    def computeAsync(self):
        # Redefine some fields
        self.redefineFileds()

        # Create the model
        model = DM.BiochemicalReactions(self)

        # Tun simulation
        model.prepareSimulation()
        model.run(self)

        # Show results
        self.storage = model.resultStorage.simulationName

        # Plot results
        model.plotODEsTxt(self.odesPlot)
        model.plotHDFResults(self.chartPlot)

    def redefineFileds(self):
        # Create tuples for species
        speciesTuples = (('time', F.Quantity('Time', default=(1, 's'))), )
        datasetColumns = ['t']
        for itSpecies in self.species:
            X = itSpecies[0]  #species variable
            speciesTuple = (('%s' % X,
                             F.Quantity('Bio_MolarConcentration',
                                        default=(1, 'M'))), )
            speciesTuples += speciesTuple
            datasetColumns.append('%s' % X)

        # Redefine Fields
        redefinedStorage = F.HdfStorage(
            hdfFile='BioReactors_SimulationResults.h5',
            hdfGroup='/BiochemicalReactions',
            datasetColumns=datasetColumns)
        self.redefineField('storage', 'storageVG', redefinedStorage)

        redefinedPlot = F.PlotView(speciesTuples,
                                   label='Plot',
                                   options={
                                       'ylabel': 'concentration',
                                       'title': ''
                                   },
                                   useHdfStorage=True,
                                   storage='storage')
        self.redefineField('plot', 'resultsVG', redefinedPlot)

        redefinedTable = F.TableView(
            speciesTuples,
            label='Table',
            options={'formats': ['0.000', '0.000', '0.000', '0.000']},
            useHdfStorage=True,
            storage='storage')
        self.redefineField('table', 'resultsVG', redefinedTable)
Exemple #3
0
class ChemostatDDE1(NumericalModel):
    label = "DDE Chemostat (Example 1)"
    description = F.ModelDescription(
        "Chemostat model with delay differential equations (DDE) - Example 1",
        show=True)
    figure = F.ModelFigure(
        src="BioReactors/img/ModuleImages/SimpleChemostat.png", show=False)

    #1. ############ Inputs ###############
    #1.1 Fields - Input values
    # Parameters
    k1 = F.Quantity(
        default=10.53,
        minValue=0,
        maxValue=1e6,
        label='k<sub>1</sub>',
        description=
        'yield coefficient related to substrate-1 consumption from bacteria-1')
    k2 = F.Quantity(
        default=28.6,
        minValue=0,
        maxValue=1e6,
        label='k<sub>2</sub>',
        description=
        'yield coefficient related to substrate-2 production from bacteria-1')
    k3 = F.Quantity(
        default=1074.,
        minValue=0,
        maxValue=1e6,
        label='k<sub>3</sub>',
        description=
        'yield coefficient related to substrate-2 consumption from bacteria-2')
    s1_in = F.Quantity('Bio_MassConcentration',
                       default=(7.5, 'g/L'),
                       label='s<sub>1</sub><sup>in</sub>',
                       description='input substrate-1 concentration')
    s2_in = F.Quantity('Bio_MassConcentration',
                       default=(75., 'g/L'),
                       label='s<sub>2</sub><sup>in</sub>',
                       description='input substrate-2 concentration')
    a = F.Quantity(
        'Fraction',
        default=(0.5, '-'),
        label='&#945',
        description=
        'proportion of organisms that are affected by the dilution rate D')
    D = F.Quantity('Bio_TimeRate',
                   default=(0.89, '1/day'),
                   minValue=(0, '1/day'),
                   label='D',
                   description='dilution rate')
    parametersFG = F.FieldGroup([s1_in, s2_in, k1, k2, k3, a, D],
                                label='Parameters')

    # Parameters - specific growth rates
    m1 = F.Quantity('Bio_TimeRate',
                    default=(1.20, '1/day'),
                    minValue=(0, '1/day'),
                    label='m<sub>1</sub>',
                    description='maximum specific growth rate of bacteria-1')
    m2 = F.Quantity('Bio_TimeRate',
                    default=(0.74, '1/day'),
                    minValue=(0, '1/day'),
                    label='m<sub>2</sub>',
                    description='maximum specific growth rate of bacteria-2')
    k_s1 = F.Quantity('Bio_MassConcentration',
                      default=(7.1, 'g/L'),
                      label='k<sub>s1</sub>',
                      description='half saturation constant of bacteria-1')
    k_s2 = F.Quantity('Bio_MassConcentration',
                      default=(9.28, 'g/L'),
                      label='k<sub>s2</sub>',
                      description='half saturation constant of bacteria-2')
    k_I = F.Quantity('Dimensionless',
                     default=(16., '-'),
                     label='k<sub>I</sub>',
                     description='constant in unit: [ sqrt( g/L ) ]')
    parametersMuFG = F.FieldGroup([m1, m2, k_s1, k_s2, k_I],
                                  label='Parameters - specific growth rates')

    # Parameters - time delays
    tau1 = F.Quantity(
        'Bio_Time',
        default=(10.1, 'day'),
        minValue=(0, 'day'),
        label='&#964<sub>1</sub>',
        description=
        'time delay in conversion of the substrate-1 to viable biomass for bacteria-1'
    )
    tau2 = F.Quantity(
        'Bio_Time',
        default=(5.7, 'day'),
        minValue=(0, 'day'),
        label='&#964<sub>2</sub>',
        description=
        'time delay in conversion of the substrate-2 to viable biomass for bacteria-2'
    )
    parametersTauFG = F.FieldGroup([tau1, tau2],
                                   label='Parameters - time delay')

    # historical initial conditions
    s1_hist_vals = F.Quantity(
        'Bio_MassConcentration',
        default=(3., 'g/L'),
        label='s<sub>1</sub><sup>&#91-&#964<sub>1</sub>, 0&#93</sup>',
        description='historical initial condition for substrate-1 concentration'
    )
    x1_hist_vals = F.Quantity(
        'Bio_MassConcentration',
        default=(0.5, 'g/L'),
        label='x<sub>1</sub><sup>&#91-&#964<sub>1</sub>, 0&#93</sup>',
        description='historical initial condition for bacteria-1 concentration'
    )
    s2_hist_vals = F.Quantity(
        'Bio_MassConcentration',
        default=(15., 'g/L'),
        label='s<sub>2</sub><sup>&#91-&#964<sub>2</sub>, 0&#93</sup>',
        description='historical initial condition for substrate-2 concentration'
    )
    x2_hist_vals = F.Quantity(
        'Bio_MassConcentration',
        default=(0.1, 'g/L'),
        label='x<sub>2</sub><sup>&#91-&#964<sub>2</sub>, 0&#93</sup>',
        description='historical initial condition for bacteria-2 concentration'
    )
    parametersHistFG = F.FieldGroup(
        [s1_hist_vals, x1_hist_vals, s2_hist_vals, x2_hist_vals],
        label='Historical initial conditions')

    inputValuesSG = F.SuperGroup(
        [parametersMuFG, parametersFG, parametersTauFG, parametersHistFG],
        label="Input values")

    #1.2 Fields - Settings
    tFinal = F.Quantity('Bio_Time',
                        default=(100, 'day'),
                        minValue=(0, 'day'),
                        maxValue=(1000, 'day'),
                        label='simulation time')
    tPrint = F.Quantity('Bio_Time',
                        default=(0.1, 'day'),
                        minValue=(1e-5, 'day'),
                        maxValue=(100, 'day'),
                        label='print interval')
    absTol = F.Quantity('Bio_Time',
                        default=(1e-12, 'day'),
                        minValue=(1e-16, 'day'),
                        maxValue=(1e-5, 'day'),
                        label='absolute tolerance')
    relTol = F.Quantity('Bio_Time',
                        default=(1e-12, 'day'),
                        minValue=(1e-16, 'day'),
                        maxValue=(1e-3, 'day'),
                        label='relative tolerance')
    solverFG = F.FieldGroup([tFinal, tPrint, absTol, relTol], label='Solver')

    settingsSG = F.SuperGroup([solverFG], label='Settings')

    #1.4 Model view
    inputView = F.ModelView(ioType="input",
                            superGroups=[inputValuesSG, settingsSG],
                            autoFetch=True)

    #2. ############ Results ###############
    dataSeries = (
        ('time', F.Quantity('Bio_Time', default=(1, 'day'))),
        ('s1', F.Quantity('Bio_MassConcentration', default=(1, 'g/L'))),
        ('x1', F.Quantity('Bio_MassConcentration', default=(1, 'g/L'))),
        ('s2', F.Quantity('Bio_MassConcentration', default=(1, 'g/L'))),
        ('x2', F.Quantity('Bio_MassConcentration', default=(1, 'g/L'))),
    )

    plot = F.PlotView(
        dataSeries,
        label='Plot',
        options={
            'ylabel': None,
            'title': 'Chemostat (DDE)'
        },
    )
    table = F.TableView(dataSeries,
                        label='Table',
                        options={
                            'title':
                            'Title',
                            'formats':
                            ['0.0000', '0.0000', '0.0000', '0.0000', '0.0000']
                        })

    chartS1S2 = F.MPLPlot(label='Chart (s<sub>1</sub>, s<sub>2</sub>)')
    chartX1X2 = F.MPLPlot(label='Chart (x<sub>1</sub>, x<sub>2</sub>)')
    chartS1X1 = F.MPLPlot(label='Chart (s<sub>1</sub>, x<sub>1</sub>)')
    chartS2X2 = F.MPLPlot(label='Chart (s<sub>2</sub>, x<sub>2</sub>)')

    resultsVG = F.ViewGroup(
        [plot, table, chartS1S2, chartX1X2, chartS1X1, chartS2X2],
        label='Results')
    resultsSG = F.SuperGroup([resultsVG], label="Results")

    # 2.2 Equilibrium point
    s1_eqpnt = F.Quantity(
        'Bio_MassConcentration',
        default=(0., 'g/L'),
        label='s<sub>1</sub><sup>*</sup>',
        description='substrate-1 concentration at the equilibrium point')
    x1_eqpnt = F.Quantity(
        'Bio_MassConcentration',
        default=(0., 'g/L'),
        label='x<sub>1</sub><sup>*</sup>',
        description='bacteria-1 concentration at the equilibrium point')
    s2_eqpnt = F.Quantity(
        'Bio_MassConcentration',
        default=(0., 'g/L'),
        label='s<sub>2</sub><sup>*</sup>',
        description='substrate-2 concentration at the equilibrium point')
    x2_eqpnt = F.Quantity(
        'Bio_MassConcentration',
        default=(0., 'g/L'),
        label='x<sub>2</sub><sup>*</sup>',
        description='bacteria-1 concentration at the equilibrium point')

    equilibriumPointFG = F.FieldGroup([s1_eqpnt, x1_eqpnt, s2_eqpnt, x2_eqpnt],
                                      label='Equilibrium point')
    equilibriumPointSG = F.SuperGroup([equilibriumPointFG],
                                      label='Equilibrium point')

    #2.1 Model view
    resultView = F.ModelView(ioType="output",
                             superGroups=[resultsSG, equilibriumPointSG])

    ############# Page structure ########
    modelBlocks = [inputView, resultView]

    def __init__(self):
        self.k1 = 10.53
        self.k2 = 28.6
        self.k3 = 1074
        self.s1_in = 7.5
        self.s2_in = 75
        self.a = 0.5
        self.m1 = 1.2
        self.m2 = 0.74
        self.k_s1 = 7.1
        self.k_s2 = 9.28
        self.k_I = 16
        self.D = 0.85
        self.tau1 = 2
        self.tau2 = 7
        self.s1_hist_vals = 2
        self.x1_hist_vals = 0.1
        self.s2_hist_vals = 10
        self.x2_hist_vals = 0.05

    def compute(self):
        chemostatDDE = DM.ChemostatDDE1(self)
        chemostatDDE.run(self)

        res = chemostatDDE.getResults()
        results = np.array(
            [res['t'], res['s1'], res['x1'], res['s2'],
             res['x2']]).transpose()
        self.plot = results
        self.table = results

        chemostatDDE.plotS1S2(self.chartS1S2)
        chemostatDDE.plotX1X2(self.chartX1X2)
        chemostatDDE.plotS1X1(self.chartS1X1)
        chemostatDDE.plotS2X2(self.chartS2X2)

        self.s1_eqpnt = (chemostatDDE.equilibriumPoint[0], 'kg/m**3')
        self.x1_eqpnt = (chemostatDDE.equilibriumPoint[1], 'kg/m**3')
        self.s2_eqpnt = (chemostatDDE.equilibriumPoint[2], 'kg/m**3')
        self.x2_eqpnt = (chemostatDDE.equilibriumPoint[3], 'kg/m**3')
Exemple #4
0
class CylindricalBlockHeatExchanger(NumericalModel):
    label = "Cylindrical heat exchanger"
    figure = F.ModelFigure(
        src="ThermoFluids/img/ModuleImages/HeatExchangerDesign.png",
        show=False)
    description = F.ModelDescription(
        """Model of heat exchanger made out of a solid cylindrical block, with
	central channels for one of the fluids and a spiraling
	channel around the block for the other fluid
	""")

    async = True
    progressOptions = {'suffix': '%', 'fractionOutput': False}

    #================ Inputs ================#
    #---------------- Fields ----------------#
    # Fields: geometry
    blockGeom = F.SubModelGroup(BlockGeometry, 'FG', 'Geometry')
    blockProps = F.SubModelGroup(BlockProperties, 'FG', 'Properties')
    blockSG = F.SuperGroup([blockGeom, blockProps], label='Block')

    # Fields: internal channels
    primaryChannelsGeom = F.SubModelGroup(ChannelGroupGeometry,
                                          'FG',
                                          label='Primary channels')
    secondaryChannelsGeom = F.SubModelGroup(ChannelGroupGeometry,
                                            'FG',
                                            label='Secondary channels')
    primaryFlowIn = F.SubModelGroup(FluidFlowInput,
                                    'FG',
                                    label='Primary flow inlet')
    secondaryFlowIn = F.SubModelGroup(FluidFlowInput,
                                      'FG',
                                      label='Secondary flow inlet')
    internalChannelSG = F.SuperGroup([
        primaryChannelsGeom, secondaryChannelsGeom, primaryFlowIn,
        secondaryFlowIn
    ],
                                     label='Channels')

    # Fields: external channel
    externalChannelGeom = F.SubModelGroup(ExternalChannelGeometry,
                                          'FG',
                                          label='Geometry')
    externalFlowIn = F.SubModelGroup(IncompressibleSolutionFlowInput,
                                     'incomSolFG',
                                     label='Inlet flow')
    externalChannelSG = F.SuperGroup([externalChannelGeom, externalFlowIn],
                                     label='External channel')

    # Fields: settings
    fvSolverSettings = F.SubModelGroup(FiniteVolumeSolverSettings,
                                       'FG',
                                       label='Finite volume solver')
    sectionResultsSettings = F.SubModelGroup(SectionResultsSettings,
                                             'FG',
                                             label='Section results plots')
    settingsSG = F.SuperGroup([fvSolverSettings, sectionResultsSettings],
                              label='Settings')

    #--------------- Model view ---------------#
    inputView = F.ModelView(ioType="input",
                            superGroups=[
                                blockSG, internalChannelSG, externalChannelSG,
                                settingsSG
                            ],
                            autoFetch=True)

    #================ Results ================#
    meshView = F.MPLPlot(label='Cross-section mesh')
    crossSectionProfileView = F.MPLPlot(label='Cross-section profile')
    longitudinalProfileView = F.MPLPlot(label='Longitudinal profile')
    primaryChannelProfileView = F.MPLPlot(label='Primary channel profile')
    secondaryChannelProfileView = F.MPLPlot(label='Secondary channel profile')
    geometryVG = F.ViewGroup([
        meshView, crossSectionProfileView, longitudinalProfileView,
        primaryChannelProfileView, secondaryChannelProfileView
    ],
                             label='Geometry/Mesh')
    geometrySG = F.SuperGroup([geometryVG], label='Geometry/mesh')

    primaryFlowOut = F.SubModelGroup(FluidFlowOutput,
                                     'FG',
                                     label='Primary flow outlet')
    secondaryFlowOut = F.SubModelGroup(FluidFlowOutput,
                                       'FG',
                                       label='Secondary flow outlet')
    externalFlowOut = F.SubModelGroup(FluidFlowOutput,
                                      'FG',
                                      label='External flow outlet')
    QDotChannels = F.SubModelGroup(HeatFlowChannels, 'FG', label='Heat flow')
    resultSG = F.SuperGroup(
        [primaryFlowOut, secondaryFlowOut, externalFlowOut, QDotChannels],
        label='Results')

    resultTable = F.TableView((
        ('xStart', F.Quantity('Length', default=(1, 'm'))),
        ('xEnd', F.Quantity('Length', default=(1, 'm'))),
        ('TPrimFluid', F.Quantity('Temperature', default=(1, 'degC'))),
        ('TPrimWall', F.Quantity('Temperature', default=(1, 'degC'))),
        ('RePrim', F.Quantity()),
        ('hConvPrim',
         F.Quantity('HeatTransferCoefficient', default=(1, 'W/m**2-K'))),
        ('QDotPrim', F.Quantity('HeatFlowRate', default=(1, 'W'))),
        ('TSecFluid', F.Quantity('Temperature', default=(1, 'degC'))),
        ('TSecWall', F.Quantity('Temperature', default=(1, 'degC'))),
        ('ReSec', F.Quantity()),
        ('hConvSec',
         F.Quantity('HeatTransferCoefficient', default=(1, 'W/m**2-K'))),
        ('QDotSec', F.Quantity('HeatFlowRate', default=(1, 'W'))),
        ('TExtFluid', F.Quantity('Temperature', default=(1, 'degC'))),
        ('TExtWall', F.Quantity('Temperature', default=(1, 'degC'))),
        ('ReExt', F.Quantity()),
        ('hConvExt',
         F.Quantity('HeatTransferCoefficient', default=(1, 'W/m**2-K'))),
        ('QDotExt', F.Quantity('HeatFlowRate', default=(1, 'W'))),
    ),
                              label='Detailed results')
    resultTPlot = F.PlotView((
        ('x', F.Quantity('Length', default=(1, 'm'))),
        ('TPrimFluid', F.Quantity('Temperature', default=(1, 'degC'))),
        ('TPrimWall', F.Quantity('Temperature', default=(1, 'degC'))),
        ('TSecFluid', F.Quantity('Temperature', default=(1, 'degC'))),
        ('TSecWall', F.Quantity('Temperature', default=(1, 'degC'))),
        ('TExtFluid', F.Quantity('Temperature', default=(1, 'degC'))),
        ('TExtWall', F.Quantity('Temperature', default=(1, 'degC'))),
    ),
                             label='Temperature plots')

    resultQPlot = F.PlotView((
        ('x', F.Quantity('Length', default=(1, 'm'))),
        ('QDotPrim', F.Quantity('HeatFlowRate', default=(1, 'W'))),
        ('QDotSec', F.Quantity('HeatFlowRate', default=(1, 'W'))),
        ('QDotExt', F.Quantity('HeatFlowRate', default=(1, 'W'))),
    ),
                             label='Heat flow plots')
    detailedResultVG = F.ViewGroup([resultTable, resultTPlot, resultQPlot],
                                   label='Detailed results')
    detailedResultSG = F.SuperGroup([detailedResultVG],
                                    label='Detailed results')

    sectionPlot1 = F.MPLPlot(label='Section 1')
    sectionPlot2 = F.MPLPlot(label='Section 2')
    sectionPlot3 = F.MPLPlot(label='Section 3')
    sectionPlot4 = F.MPLPlot(label='Section 4')
    sectionPlot5 = F.MPLPlot(label='Section 5')
    sectionResultsVG = F.ViewGroup(
        [sectionPlot1, sectionPlot2, sectionPlot3, sectionPlot4, sectionPlot5],
        label='Section temperatures')
    sectionResultsSG = F.SuperGroup([sectionResultsVG],
                                    label='Section results')

    resultView = F.ModelView(
        ioType="output",
        superGroups=[geometrySG, resultSG, detailedResultSG, sectionResultsSG])

    ############# Page structure ########
    modelBlocks = [inputView, resultView]

    ############# Methods ###############
    def __init__Internal(self):
        self.blockGeom.diameter = (58.0, 'mm')
        self.blockGeom.length = (0.8, 'm')
        self.blockProps.divisionStep = (0.1, 'm')
        self.blockProps.material = 'Aluminium6061'

        self.primaryChannelsGeom.number = 3
        self.primaryChannelsGeom.radialPosition = (7, 'mm')
        self.primaryChannelsGeom.startingAngle = (0, 'deg')
        self.primaryChannelsGeom.meshFineness = 4
        self.primaryChannelsGeom.channelName = "PrimaryChannel"

        self.primaryChannelsGeom.externalDiameter = (7.5, 'mm')
        self.primaryChannelsGeom.sections[0] = (0.002, 0.2)
        self.primaryChannelsGeom.sections[1] = (0.004, 0.1)
        self.primaryChannelsGeom.sections[2] = (0.006, 0.1)
        self.primaryChannelsGeom.sections[3] = (0.0065, 0.1)
        self.primaryChannelsGeom.sections[4] = (0.007, 0.3)

        self.secondaryChannelsGeom.number = 3
        self.secondaryChannelsGeom.radialPosition = (17.5, 'mm')
        self.secondaryChannelsGeom.startingAngle = (60, 'deg')
        self.secondaryChannelsGeom.meshFineness = 4
        self.secondaryChannelsGeom.channelName = "SecondaryChannel"

        self.secondaryChannelsGeom.externalDiameter = (7.5, 'mm')
        self.secondaryChannelsGeom.sections[0] = (0.002, 0.2)
        self.secondaryChannelsGeom.sections[1] = (0.004, 0.1)
        self.secondaryChannelsGeom.sections[2] = (0.006, 0.1)
        self.secondaryChannelsGeom.sections[3] = (0.0065, 0.1)
        self.secondaryChannelsGeom.sections[4] = (0.007, 0.3)

        self.primaryFlowIn.fluidName = 'ParaHydrogen'
        self.primaryFlowIn.flowRateChoice = 'm'
        self.primaryFlowIn.mDot = (1, 'kg/h')
        self.primaryFlowIn.T = (100, 'K')
        self.primaryFlowIn.p = (1, 'bar')

        self.secondaryFlowIn.fluidName = 'ParaHydrogen'
        self.secondaryFlowIn.flowRateChoice = 'm'
        self.secondaryFlowIn.mDot = (1, 'kg/h')
        self.secondaryFlowIn.T = (100, 'K')
        self.secondaryFlowIn.p = (1, 'bar')

        self.externalFlowIn.solName = 'MEG'
        self.externalFlowIn.solMassFraction = (50, '%')
        self.externalFlowIn.flowRateChoice = 'V'
        self.externalFlowIn.VDot = (3, 'm**3/h')
        self.externalFlowIn.T = (80, 'degC')
        self.externalFlowIn.p = (1, 'bar')

        self.externalChannelGeom.widthAxial = (30, 'mm')
        self.externalChannelGeom.heightRadial = (12, 'mm')
        self.externalChannelGeom.coilPitch = (32, 'mm')
        self.externalChannelGeom.meshFineness = 4

        self.sectionResultsSettings.setTRange = False
        self.sectionResultsSettings.Tmin = (100, 'K')
        self.sectionResultsSettings.Tmax = (380, 'K')

    def __init__(self):
        self.blockGeom.diameter = (60.0, 'mm')
        self.blockGeom.length = (1.0, 'm')
        self.blockProps.divisionStep = (0.1, 'm')
        self.blockProps.material = 'Aluminium6061'

        self.primaryChannelsGeom.number = 4
        self.primaryChannelsGeom.radialPosition = (10, 'mm')
        self.primaryChannelsGeom.startingAngle = (0, 'deg')
        self.primaryChannelsGeom.meshFineness = 3
        self.primaryChannelsGeom.channelName = "PrimaryChannel"

        self.primaryChannelsGeom.externalDiameter = (10, 'mm')
        self.primaryChannelsGeom.sections[0] = (0.000, 0.2)
        self.primaryChannelsGeom.sections[1] = (0.002, 0.2)
        self.primaryChannelsGeom.sections[2] = (0.004, 0.2)
        self.primaryChannelsGeom.sections[3] = (0.006, 0.2)
        self.primaryChannelsGeom.sections[4] = (0.008, 0.2)

        self.secondaryChannelsGeom.number = 6
        self.secondaryChannelsGeom.radialPosition = (22.5, 'mm')
        self.secondaryChannelsGeom.startingAngle = (60, 'deg')
        self.secondaryChannelsGeom.meshFineness = 3
        self.secondaryChannelsGeom.channelName = "SecondaryChannel"

        self.secondaryChannelsGeom.externalDiameter = (7, 'mm')
        self.secondaryChannelsGeom.sections[0] = (0.002, 0.2)
        self.secondaryChannelsGeom.sections[1] = (0.003, 0.2)
        self.secondaryChannelsGeom.sections[2] = (0.004, 0.2)
        self.secondaryChannelsGeom.sections[3] = (0.005, 0.2)
        self.secondaryChannelsGeom.sections[4] = (0.006, 0.2)

        self.primaryFlowIn.fluidName = 'Ammonia'
        self.primaryFlowIn.flowRateChoice = 'm'
        self.primaryFlowIn.mDot = (50, 'kg/h')
        self.primaryFlowIn.T = (400, 'K')
        self.primaryFlowIn.p = (20, 'bar')

        self.secondaryFlowIn.fluidName = 'Ammonia'
        self.secondaryFlowIn.flowRateChoice = 'm'
        self.secondaryFlowIn.mDot = (50, 'kg/h')
        self.secondaryFlowIn.T = (400, 'K')
        self.secondaryFlowIn.p = (20, 'bar')

        self.externalFlowIn.solName = 'MPG'
        self.externalFlowIn.solMassFraction = (50, '%')
        self.externalFlowIn.flowRateChoice = 'V'
        self.externalFlowIn.VDot = (0.5, 'm**3/h')
        self.externalFlowIn.T = (20, 'degC')
        self.externalFlowIn.p = (1, 'bar')

        self.externalChannelGeom.widthAxial = (40, 'mm')
        self.externalChannelGeom.heightRadial = (10, 'mm')
        self.externalChannelGeom.coilPitch = (43, 'mm')
        self.externalChannelGeom.meshFineness = 3

        self.sectionResultsSettings.setTRange = False
        self.sectionResultsSettings.Tmin = (200, 'K')
        self.sectionResultsSettings.Tmax = (350, 'K')

    def computeAsync(self):
        # PreComputation
        self.externalChannelGeom.compute(self.blockGeom.diameter)
        self.primaryChannelsGeom.compute()
        self.secondaryChannelsGeom.compute()

        self.primaryFlowIn.compute()
        self.secondaryFlowIn.compute()
        self.externalFlowIn.compute()

        # Validation
        self.validateInputs()

        # Create the mesh
        mesher = HeatExchangerMesher()
        mesher.create(self)

        # Create channel calculator objects
        solver = HeatExchangerSolver(self, mesher)
        solver.createChannelCalculators(self)

        # Produce geometry/mesh drawings
        self.drawGeometry(mesher, solver)

        solver.solve(self)

        # Produce results
        self.postProcess(mesher, solver)

    def drawGeometry(self, mesher, solver):
        # Draw the mesh
        vertexCoords = mesher.mesh.vertexCoords
        vertexIDs = mesher.mesh._orderedCellVertexIDs
        triPlotMesh = tri.Triangulation(vertexCoords[0], vertexCoords[1],
                                        np.transpose(vertexIDs))
        self.meshView.triplot(triPlotMesh)
        self.meshView.set_aspect('equal')
        self.meshView.set_title('%d elements' %
                                len(mesher.mesh.cellCenters[0]))

        # Draw the channels profiles
        ticks = ticker.FuncFormatter(lambda x, pos: '{0:g}'.format(x * 1e3))

        solver.primChannelCalc.plotGeometry(self.primaryChannelProfileView)
        self.primaryChannelProfileView.set_xlabel('[m]')
        self.primaryChannelProfileView.set_ylabel('[mm]')
        self.primaryChannelProfileView.yaxis.set_major_formatter(ticks)

        solver.secChannelCalc.plotGeometry(self.secondaryChannelProfileView)
        self.secondaryChannelProfileView.set_xlabel('[m]')
        self.secondaryChannelProfileView.set_ylabel('[mm]')
        self.secondaryChannelProfileView.yaxis.set_major_formatter(ticks)

        #Draw the cross-section profile
        crossSectionProfile = HeatExchangerCrossSectionProfile()
        crossSectionProfile.addBlock(self.blockGeom)
        crossSectionProfile.addChannels(self.primaryChannelsGeom)
        crossSectionProfile.addChannels(self.secondaryChannelsGeom)
        crossSectionProfile.plotGeometry(self.crossSectionProfileView)

        self.crossSectionProfileView.set_xlabel('[mm]')
        self.crossSectionProfileView.set_ylabel('[mm]')
        self.crossSectionProfileView.xaxis.set_major_formatter(ticks)
        self.crossSectionProfileView.yaxis.set_major_formatter(ticks)

        #Draw the longitudinal profile
        longitudinalProfile = HeatExchangerLongitudinalProfile()
        longitudinalProfile.addBlock(self.blockGeom)
        longitudinalProfile.addExternalChannel(self.externalChannelGeom)
        longitudinalProfile.plotGeometry(self.longitudinalProfileView)

        self.longitudinalProfileView.set_xlabel('[mm]')
        self.longitudinalProfileView.set_ylabel('[mm]')
        self.longitudinalProfileView.xaxis.set_major_formatter(ticks)
        self.longitudinalProfileView.yaxis.set_major_formatter(ticks)

    def postProcess(self, mesher, solver):
        # Get the value for the outlet conditions
        self.primaryFlowOut.compute(fState=solver.primChannelStateOut,
                                    mDot=self.primaryFlowIn.mDot)
        self.secondaryFlowOut.compute(fState=solver.secChannelStateOut,
                                      mDot=self.secondaryFlowIn.mDot)
        self.externalFlowOut.compute(fState=solver.extChannelStateOut,
                                     mDot=self.externalFlowIn.mDot)

        self.QDotChannels.QDotPrimaryChannels = self.primaryFlowIn.mDot * \
         abs(self.primaryFlowOut.fState.h - self.primaryFlowIn.fState.h)
        self.QDotChannels.QDotSecondaryChannels = self.secondaryFlowIn.mDot * \
         abs(self.secondaryFlowOut.fState.h - self.secondaryFlowIn.fState.h)
        self.QDotChannels.QDotExternalChannel = self.externalFlowIn.mDot * \
         abs(self.externalFlowOut.fState.h - self.externalFlowIn.fState.h)
        # Fill the table with values
        self.resultTable.resize(solver.numSectionSteps)
        self.resultTPlot.resize(solver.numSectionSteps)
        self.resultQPlot.resize(solver.numSectionSteps)

        for i in range(solver.numSectionSteps):
            self.resultTable[i] = (
                solver.primChannelCalc.sections[i].xStart,
                solver.primChannelCalc.sections[i].xEnd,
                solver.primChannelCalc.sections[i].fState.T,
                solver.primChannelCalc.sections[i].TWall,
                solver.primChannelCalc.sections[i].Re,
                solver.primChannelCalc.sections[i].hConv,
                abs(solver.primChannelCalc.sections[i].QDotWall),
                solver.secChannelCalc.sections[i].fState.T,
                solver.secChannelCalc.sections[i].TWall,
                solver.secChannelCalc.sections[i].Re,
                solver.secChannelCalc.sections[i].hConv,
                abs(solver.secChannelCalc.sections[i].QDotWall),
                solver.extChannelCalc.sections[i].fState.T,
                solver.extChannelCalc.sections[i].TWall,
                solver.extChannelCalc.sections[i].Re,
                solver.extChannelCalc.sections[i].hConv,
                abs(solver.extChannelCalc.sections[i].QDotWall),
            )

            self.resultTPlot[i] = (
                (solver.primChannelCalc.sections[i].xStart +
                 solver.primChannelCalc.sections[i].xEnd) / 2,
                solver.primChannelCalc.sections[i].fState.T,
                solver.primChannelCalc.sections[i].TWall,
                solver.secChannelCalc.sections[i].fState.T,
                solver.secChannelCalc.sections[i].TWall,
                solver.extChannelCalc.sections[i].fState.T,
                solver.extChannelCalc.sections[i].TWall,
            )

            self.resultQPlot[i] = (
                (solver.primChannelCalc.sections[i].xStart +
                 solver.primChannelCalc.sections[i].xEnd) / 2,
                abs(solver.primChannelCalc.sections[i].QDotWall),
                abs(solver.secChannelCalc.sections[i].QDotWall),
                abs(solver.extChannelCalc.sections[i].QDotWall),
            )

    def validateInputs(self):
        self.validateChannels(self.primaryChannelsGeom, "primary")
        self.validateChannels(self.secondaryChannelsGeom, "secondary")

        if self.externalChannelGeom.widthAxial > self.externalChannelGeom.coilPitch:
            raise ValueError(
                'The coil pitch of the external channel is less than the axial width.'
            )

    def validateChannels(self, channelsGeom, channelsName):
        if self.blockGeom.diameter <= channelsGeom.externalDiameter:
            raise ValueError(
                'The block diameter is less than the external diameter of the {0} channels.'
                .format(channelsName))

        if self.blockGeom.diameter / 2. <= (
                channelsGeom.radialPosition +
                channelsGeom.externalDiameter / 2.):
            raise ValueError(
                'The radial position of the {0} channels is too big (the channels are outside of the block).'
                .format(channelsName))

        if self.blockGeom.length != channelsGeom.length:
            raise ValueError(
                'The length of the block is different from the length of the {0} channels.'
                .format(channelsName))

        i = 0
        for section in channelsGeom.sections:
            if (section['length'] < self.blockProps.divisionStep):
                raise ValueError(
                    'The axial division step of the block is greater than the {0}-th section of the {1} channels'
                    .format(i, channelsName))

            if self.isDividedExactly(section['length'],
                                     self.blockProps.divisionStep) == False:
                raise ValueError(
                    'The axial division step of the block does not divide the {0}-th section of the {1} channels in equal parts.'
                    .format(i, channelsName))
            i += 1

    def isDividedExactly(self, a, b):
        bigE = 1e6
        return int(a * bigE) % int(b * bigE) == 0