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='α', 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='τ<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='τ<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>[-τ<sub>1</sub>, 0]</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>[-τ<sub>1</sub>, 0]</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>[-τ<sub>2</sub>, 0]</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>[-τ<sub>2</sub>, 0]</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='ε*', 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='ε<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')
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)
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='α', 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='τ<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='τ<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>[-τ<sub>1</sub>, 0]</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>[-τ<sub>1</sub>, 0]</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>[-τ<sub>2</sub>, 0]</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>[-τ<sub>2</sub>, 0]</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')
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