示例#1
0
class PowerPlant():

    def __init__(self, working_fluid):
        """Set up model."""
        self.working_fluid = working_fluid
        fluids = ['water', self.working_fluid, 'air']
        self.nw = Network(fluids=fluids)
        self.nw.set_attr(p_unit='bar', T_unit='C', h_unit='kJ / kg')

        # geo parameters

        self.geo_mass_flow = 200
        geo_steam_share = 0.1
        self.T_brine_in = 140

        # ambient parameters

        self.T_amb = 5
        self.p_amb = 0.6

        # main components

        geo_steam = Source('geosteam source')
        geo_brine = Source('geobrine source')
        geo_reinjection = Sink('re-injection')

        air_in = Source('air source')
        air_out = Sink('air sink')
        air_fan = Compressor('air fan')
        air_cond = Condenser('condenser')

        orc_cc = CycleCloser('orc cycle closer')

        evap_splitter = Splitter('splitter evaporation')
        evap_merge = Merge('merge evaporation')
        evap_steam = Condenser('geosteam evaporator')
        evap_brine = HeatExchanger('geobrine evaporator')
        dr = Drum('drum')
        geo_merge = Merge('merge brine')

        pre = HeatExchanger('preheater')
        feed_working_fluid_pump = Pump('feed pump')

        tur = Turbine('turbine')

        ihe = HeatExchanger('internal heat exchanger')

        # busses
        net_power = Bus('net power output')
        net_power.add_comps(
            {'comp': tur, 'char': 0.97},
            {'comp': feed_working_fluid_pump, 'char': 0.97, 'base': 'bus'},
            {'comp': air_fan, 'char': 0.97, 'base': 'bus'}
        )

        ORC_power_bus = Bus('cycle gross power output')
        ORC_power_bus.add_comps(
            {'comp': tur}, {'comp': feed_working_fluid_pump}
        )

        geothermal_bus = Bus('thermal input')
        geothermal_bus.add_comps(
            {'comp': pre, 'char': -1}, {'comp': evap_brine, 'char': -1},
            {'comp': evap_steam, 'char': -1}
        )

        self.nw.add_busses(net_power, ORC_power_bus, geothermal_bus)

        # turbine to condenser
        c1 = Connection(orc_cc, 'out1', tur, 'in1', label='1')
        c2 = Connection(tur, 'out1', ihe, 'in1', label='2')
        c3 = Connection(ihe, 'out1', air_cond, 'in1', label='3')
        self.nw.add_conns(c1, c2, c3)

        # condenser to steam generator
        c4 = Connection(air_cond, 'out1', feed_working_fluid_pump, 'in1', label='4')
        c5 = Connection(feed_working_fluid_pump, 'out1', ihe, 'in2', label='5')
        self.nw.add_conns(c4, c5)

        # steam generator
        c6 = Connection(ihe, 'out2', pre, 'in2', label='6')
        c7 = Connection(pre, 'out2', dr, 'in1', label='7')
        c8 = Connection(dr, 'out1', evap_splitter, 'in1', label='8')
        c9 = Connection(evap_splitter, 'out2', evap_steam, 'in2', label='9')
        c10 = Connection(evap_steam, 'out2', evap_merge, 'in2', label='10')
        c11 = Connection(evap_splitter, 'out1', evap_brine, 'in2', label='11')
        c12 = Connection(evap_brine, 'out2', evap_merge, 'in1', label='12')
        c13 = Connection(evap_merge, 'out1', dr, 'in2', label='13')
        c0 = Connection(dr, 'out2', orc_cc, 'in1', label='0')
        self.nw.add_conns(c6, c7, c8, c11, c9, c12, c10, c13, c0)

        # condenser cold side
        c20 = Connection(air_in, 'out1', air_fan, 'in1', label='20')
        c21 = Connection(air_fan, 'out1', air_cond, 'in2', label='21')
        c22 = Connection(air_cond, 'out2', air_out, 'in1', label='22')
        self.nw.add_conns(c20, c21, c22)

        # geo source
        c30 = Connection(geo_steam, 'out1', evap_steam, 'in1', label='30')
        c31 = Connection(evap_steam, 'out1',  geo_merge, 'in1', label='31')
        c32 = Connection(geo_brine, 'out1', geo_merge, 'in2', label='32')
        c33 = Connection(geo_merge, 'out1', evap_brine, 'in1', label='33')
        self.nw.add_conns(c30, c31, c32, c33)

        c34 = Connection(evap_brine, 'out1', pre, 'in1', label='34')
        c35 = Connection(pre, 'out1', geo_reinjection, 'in1', label='35')
        self.nw.add_conns(c34, c35)

        # generate a set of stable starting values of every working fluid
        # fluid settings
        c6.set_attr(fluid={self.working_fluid: 1.0, 'air': 0.0, 'water': 0.0})
        c20.set_attr(fluid={self.working_fluid: 0.0, 'air': 1.0, 'water': 0.0})
        c30.set_attr(fluid={self.working_fluid: 0.0, 'air': 0.0, 'water': 1.0})
        c32.set_attr(fluid={self.working_fluid: 0.0, 'air': 0.0, 'water': 1.0})

        # connection parameters
        p0 = PSI('P', 'T', self.T_brine_in + 273.15, 'Q', 1, self.working_fluid)
        c1.set_attr(p0=p0 / 1e5)
        ws_stable_h0 = (
            PSI('H', 'T', self.T_amb + 273.15, 'Q', 1, self.working_fluid) +
            0.5 * (
                PSI('H', 'T', self.T_brine_in + 273.15, 'Q', 1, self.working_fluid) -
                PSI('H', 'T', self.T_amb + 273.15, 'Q', 1, self.working_fluid)
            )
        ) / 1e3
        c2.set_attr(h=ws_stable_h0)
        p0 = PSI('P', 'T', self.T_amb + 273.15, 'Q', 1, self.working_fluid)
        c3.set_attr(Td_bp=5, design=['Td_bp'], p0=p0 / 1e5)
        c5.set_attr(h=Ref(c4, 1, 1))

        # steam generator
        c30.set_attr(
            m=self.geo_mass_flow * geo_steam_share,
            T=self.T_brine_in, x=1, p0=5)
        c32.set_attr(
            m=self.geo_mass_flow * (1 - geo_steam_share),
            T=self.T_brine_in, x=0)

        c13.set_attr()
        c12.set_attr(x=0.5)
        c10.set_attr(x=0.5, design=['x'])
        c34.set_attr(h=Ref(c33, 1, -50))

        c7.set_attr(Td_bp=-2)

        # main condenser
        c20.set_attr(p=self.p_amb, T=self.T_amb)
        c22.set_attr(T=self.T_amb + 15, p=self.p_amb)

        # component parameters
        # condensing
        ihe.set_attr(pr1=0.98, pr2=0.98)
        air_cond.set_attr(pr1=1, pr2=0.995, ttd_u=10)
        air_fan.set_attr(eta_s=0.6)

        # steam generator
        evap_brine.set_attr(pr1=0.98, ttd_l=8)
        pre.set_attr(pr1=0.98, pr2=0.98)

        self.nw.set_attr(iterinfo=False)
        self.nw.solve('design')
        self.nw.save('stable_' + self.working_fluid)

        # specify actual parameters
        tur.set_attr(eta_s=0.9)
        feed_working_fluid_pump.set_attr(eta_s=0.75)
        c2.set_attr(h=None)
        c5.set_attr(h=None)
        c34.set_attr(h=None, T=Ref(c33, 1, -10))

        self.nw.solve('design')
        c22.set_attr(T=None)
        c3.set_attr(Td_bp=None)

        self.ude_IHE_size = UserDefinedEquation(
            label='ihe deshuperheat ratio',
            func=desuperheat, deriv=desuperheat_deriv,
            latex={
                'equation':
                    r'0 = h_3 - h_2 - x_\mathrm{IHE} \cdot \left(h_3 -'
                    r'h\left(p_2, T_5 + \Delta T_\mathrm{t,u,min} \right)'
                    r'\right)'},
            conns=[
                self.nw.get_conn('2'),
                self.nw.get_conn('3'),
                self.nw.get_conn('5')],
            params={'distance': 0.0, 'ttd_min': 2}
        )
        if self.nw.lin_dep or self.nw.res[-1] > 1e-3:
            msg = 'No stable solution found.'
            raise TESPyNetworkError(msg)
        print(
            'Generated stable starting values for working fluid ' +
            self.working_fluid + '.')

    def run_simulation(
            self, p_before_tur=None, Q_ihe=None, Q_brine_ev=None,
            T_before_tur=None, T_reinjection=None, brine_evap_Td=None,
            dT_air=None, IHE_sizing=None, geo_steam_share=None):
        """Run simulation on specified parameter set."""

        self.nw.get_comp('internal heat exchanger').set_attr(Q=Q_ihe)
        self.nw.get_conn('1').set_attr(p=p_before_tur, T=T_before_tur)
        self.nw.get_conn('35').set_attr(T=T_reinjection)
        self.nw.get_comp('geobrine evaporator').set_attr(Q=Q_brine_ev)

        if geo_steam_share is not None:
            self.nw.get_conn('30').set_attr(
                m=self.geo_mass_flow * geo_steam_share)
            self.nw.get_conn('32').set_attr(
                m=self.geo_mass_flow * (1 - geo_steam_share))

        if brine_evap_Td is not None:
            self.nw.get_conn('34').set_attr(
                T=Ref(self.nw.get_conn('33'), 1, brine_evap_Td))
        else:
            self.nw.get_conn('34').set_attr(T=None)

        if dT_air is not None:
            self.nw.get_conn('22').set_attr(T=Ref(self.nw.get_conn('21'), 1, dT_air))
        else:
            self.nw.get_conn('22').set_attr(T=None)

        if IHE_sizing is None:
            if self.ude_IHE_size in self.nw.user_defined_eq.values():
                self.nw.del_ude(self.ude_IHE_size)
            self.nw.get_comp('internal heat exchanger').set_attr(pr1=0.98, pr2=0.98)
        else:
            if self.ude_IHE_size not in self.nw.user_defined_eq.values():
                self.nw.add_ude(self.ude_IHE_size)
            self.ude_IHE_size.params['distance'] = IHE_sizing
            if IHE_sizing == 0:
                self.nw.get_comp('internal heat exchanger').set_attr(pr1=1, pr2=1)
            else:
                self.nw.get_comp('internal heat exchanger').set_attr(pr1=0.98, pr2=0.98)

        try:
            self.nw.solve('design')
#            self.nw.print_results()
        except ValueError:
            self.nw.res = [1]
            pass

    def check_simulation(self, value):
        """Check if simulation converged."""
        if self.nw.lin_dep or self.nw.res[-1] > 1e-3:
            self.nw.solve(
                'design', init_path='stable_' + self.working_fluid,
                init_only=True)
            return np.nan
        else:
            for cp in self.nw.comps['object']:
                if isinstance(cp, HeatExchanger):
                    if cp.Q.val > 0:
                        print(cp.label)
                        return np.nan
                    elif cp.kA.val <= 0 or (np.isnan(cp.kA.val) and cp.Q.val != 0):
                        print(cp.label)
                        return np.nan
        return value

    def get_power(self):
        """Calculate ORC gross power (main cycle only)."""
        return self.check_simulation(self.nw.busses['cycle gross power output'].P.val)

    def get_net_power(self):
        """Calculate net power."""
        return self.check_simulation(self.nw.busses['net power output'].P.val)

    def get_thermal_efficiency(self):
        """Calculate thermal efficiency."""
        return self.check_simulation(
            -self.nw.busses['cycle gross power output'].P.val /
            self.nw.busses['thermal input'].P.val)

    def get_net_efficiency(self):
        """Calculate net efficiency."""
        return self.check_simulation(
            -self.nw.busses['net power output'].P.val /
            self.nw.busses['thermal input'].P.val)

    def get_geosteam_share(self):
        """Return a geosteam share."""
        return self.check_simulation(
            self.nw.get_conn('geosteam').m.val_SI / self.geo_mass_flow)

    def get_connection_param(self, conn, param):
        """Return a connection parameter."""
        return self.check_simulation(
            self.nw.get_conn(conn).get_attr(param).val)

    def get_component_param(self, comp, param):
        """Return a component parameter."""
        return self.check_simulation(
            self.nw.get_comp(comp).get_attr(param).val)

    def get_misc_param(self, param):
        """Get non component or connection parameters."""
        if param == 'gross power output':
            return self.get_power()
        elif param == 'net power output':
            return self.get_net_power()
        elif param == 'thermal efficiency':
            return self.get_thermal_efficiency()
        elif param == 'net efficiency':
            return self.get_net_efficiency()
        elif param == 'IHE sizing factor':
            return self.ude_IHE_size.params['distance']

    def get_objective_func(self, objective):
        """Return corresponding objective function."""
        if objective == 'net power output':
            return self.get_net_power
        elif objective == 'gross power output':
            return self.get_power
        else:
            msg = (
                'Please specify valid objective function: "net power output" '
                'or "gross power output".')
            raise ValueError(msg)
class TestFluidPropertyBackEnds:
    """Testing full models with different fluid property back ends."""

    def setup_clausius_rankine(self, fluid_list):
        """Setup a Clausius-Rankine cycle."""
        self.nw = Network(fluids=fluid_list)
        self.nw.set_attr(p_unit='bar', T_unit='C', iterinfo=True)

        # %% components

        # main components
        turb = Turbine('turbine')
        con = Condenser('condenser')
        pu = Pump('pump')
        steam_generator = HeatExchangerSimple('steam generator')
        closer = CycleCloser('cycle closer')

        # cooling water
        so_cw = Source('cooling water inlet')
        si_cw = Sink('cooling water outlet')

        # %% connections

        # main cycle
        fs_in = Connection(closer, 'out1', turb, 'in1', label='livesteam')
        ws = Connection(turb, 'out1', con, 'in1', label='wastesteam')
        cond = Connection(con, 'out1', pu, 'in1', label='condensate')
        fw = Connection(pu, 'out1', steam_generator, 'in1', label='feedwater')
        fs_out = Connection(steam_generator, 'out1', closer, 'in1')
        self.nw.add_conns(fs_in, ws, cond, fw, fs_out)

        # cooling water
        cw_in = Connection(so_cw, 'out1', con, 'in2')
        cw_out = Connection(con, 'out2', si_cw, 'in1')
        self.nw.add_conns(cw_in, cw_out)

        # %% parametrization of components

        turb.set_attr(eta_s=0.9)
        con.set_attr(pr1=1, pr2=0.99, ttd_u=5)
        steam_generator.set_attr(pr=0.9)

        # %% parametrization of connections

        fs_in.set_attr(p=100, T=500, m=100, fluid={self.nw.fluids[0]: 1})
        fw.set_attr(h=200e3)
        cw_in.set_attr(T=20, p=5, fluid={self.nw.fluids[0]: 1})
        cw_out.set_attr(T=30)

        # %% solving
        self.nw.solve('design')
        pu.set_attr(eta_s=0.7)
        fw.set_attr(h=None)
        self.nw.solve('design')

    def setup_pipeline_network(self, fluid_list):
        """Setup a pipeline network."""
        self.nw = Network(fluids=fluid_list)
        self.nw.set_attr(p_unit='bar', T_unit='C', iterinfo=False)

        # %% components

        # main components
        pu = Pump('pump')
        pi = Pipe('pipeline')
        es = HeatExchangerSimple('energy balance closing')

        closer = CycleCloser('cycle closer')

        pu_pi = Connection(pu, 'out1', pi, 'in1')
        pi_es = Connection(pi, 'out1', es, 'in1')
        es_closer = Connection(es, 'out1', closer, 'in1')
        closer_pu = Connection(closer, 'out1', pu, 'in1')
        self.nw.add_conns(pu_pi, pi_es, es_closer, closer_pu)

        # %% parametrization of components

        pu.set_attr(eta_s=0.7)
        pi.set_attr(pr=0.95, L=100, ks=1e-5, D='var', Q=0)
        es.set_attr(pr=1)

        # %% parametrization of connections

        pu_pi.set_attr(p=20, T=100, m=10, fluid={self.nw.fluids[0]: 1})

        # %% solving
        self.nw.solve('design')

    @pytest.mark.skipif(
        os.environ.get('TRAVIS') == 'true',
        reason='Travis CI cannot handle the tabular CoolProp back ends, '
        'skipping this test. The test should run on your local machine.')
    def test_clausius_rankine_tabular(self):
        """Test the Clausius-Rankine cycle with different back ends."""
        fluid = 'water'
        back_ends = ['HEOS', 'BICUBIC', 'TTSE']
        results = {}
        for back_end in back_ends:
            # delete the fluid from the memorisation class
            if fluid in fp.Memorise.state.keys():
                del fp.Memorise.state[fluid]
                del fp.Memorise.back_end[fluid]
            self.setup_clausius_rankine([back_end + '::' + fluid])
            results[back_end] = (
                1 - abs(self.nw.get_comp('condenser').Q.val) /
                self.nw.get_comp('steam generator').Q.val)

        efficiency = results['HEOS']

        if fluid in fp.Memorise.state.keys():
            del fp.Memorise.state[fluid]
            del fp.Memorise.back_end[fluid]
        for back_end in back_ends:
            if back_end == 'HEOS':
                continue

            d_rel = (abs(results[back_end] - efficiency) / efficiency)

            msg = (
                'The deviation in thermal efficiency of the Clausius-Rankine '
                'cycle calculated with ' + back_end + ' back end is ' +
                str(d_rel) + ' but should not be larger than 1e-4.')
            assert d_rel <= 1e-4, msg

    def test_clausius_rankine(self):
        """Test the Clausius-Rankine cycle with different back ends."""
        fluid = 'water'
        back_ends = ['HEOS', 'IF97']
        results = {}
        for back_end in back_ends:
            # delete the fluid from the memorisation class
            if fluid in fp.Memorise.state.keys():
                del fp.Memorise.state[fluid]
                del fp.Memorise.back_end[fluid]
            self.setup_clausius_rankine([back_end + '::' + fluid])
            results[back_end] = (
                1 - abs(self.nw.get_comp('condenser').Q.val) /
                self.nw.get_comp('steam generator').Q.val)

        efficiency = results['HEOS']

        if fluid in fp.Memorise.state.keys():
            del fp.Memorise.state[fluid]
            del fp.Memorise.back_end[fluid]
        for back_end in back_ends:
            if back_end == 'HEOS':
                continue

            d_rel = (abs(results[back_end] - efficiency) / efficiency)

            msg = (
                'The deviation in thermal efficiency of the Clausius-Rankine '
                'cycle calculated with ' + back_end + ' back end is ' +
                str(d_rel) + ' but should not be larger than 1e-4.')
            assert d_rel <= 1e-4, msg

    def test_pipeline_network(self):
        """Test a pipeline network with fluids from different back ends."""
        fluids_back_ends = {'DowJ': 'INCOMP', 'water': 'HEOS'}

        for fluid, back_end in fluids_back_ends.items():
            # delete the fluid from the memorisation class
            if fluid in fp.Memorise.state.keys():
                del fp.Memorise.state[fluid]
            self.setup_pipeline_network([back_end + '::' + fluid])
            convergence_check(self.nw.lin_dep)

            value = round(self.nw.get_comp('pipeline').pr.val, 5)
            msg = (
                'The pressure ratio of the pipeline must be at 0.95, but '
                'is at ' + str(value) + ' for the fluid ' + fluid + '.')
            assert value == 0.95, msg
            value = round(self.nw.get_comp('pump').pr.val, 5)
            msg = (
                'The pressure ratio of the pipeline must be at ' +
                str(round(1 / 0.95, 5)) + ', but is at ' + str(value) +
                ' for the fluid ' + fluid + '.')
            assert value == round(1 / 0.95, 5), msg
示例#3
0
class TestClausiusRankine:
    def setup(self):
        """Set up clausis rankine cycle with turbine driven feed water pump."""
        self.Tamb = 20
        self.pamb = 1
        fluids = ['water']
        self.nw = Network(fluids=fluids)
        self.nw.set_attr(p_unit='bar', T_unit='C', h_unit='kJ / kg')

        # create components
        splitter1 = Splitter('splitter 1')
        merge1 = Merge('merge 1')
        turb = Turbine('turbine')
        fwp_turb = Turbine('feed water pump turbine')
        condenser = HeatExchangerSimple('condenser')
        fwp = Pump('pump')
        steam_generator = HeatExchangerSimple('steam generator')
        cycle_close = CycleCloser('cycle closer')

        # create busses
        # power output bus
        self.power = Bus('power_output')
        self.power.add_comps({'comp': turb, 'char': 1})
        # turbine driven feed water pump internal bus
        self.fwp_power = Bus('feed water pump power', P=0)
        self.fwp_power.add_comps({
            'comp': fwp_turb,
            'char': 1
        }, {
            'comp': fwp,
            'char': 1,
            'base': 'bus'
        })
        # heat input bus
        self.heat = Bus('heat_input')
        self.heat.add_comps({'comp': steam_generator, 'base': 'bus'})
        self.nw.add_busses(self.power, self.fwp_power, self.heat)

        # create connections
        fs_in = Connection(cycle_close, 'out1', splitter1, 'in1', label='fs')
        fs_fwpt = Connection(splitter1, 'out1', fwp_turb, 'in1')
        fs_t = Connection(splitter1, 'out2', turb, 'in1')
        fwpt_ws = Connection(fwp_turb, 'out1', merge1, 'in1')
        t_ws = Connection(turb, 'out1', merge1, 'in2')
        ws = Connection(merge1, 'out1', condenser, 'in1')
        cond = Connection(condenser, 'out1', fwp, 'in1', label='cond')
        fw = Connection(fwp, 'out1', steam_generator, 'in1', label='fw')
        fs_out = Connection(steam_generator, 'out1', cycle_close, 'in1')
        self.nw.add_conns(fs_in, fs_fwpt, fs_t, fwpt_ws, t_ws, ws, cond, fw,
                          fs_out)

        # component parameters
        turb.set_attr(eta_s=1)
        fwp_turb.set_attr(eta_s=1)
        condenser.set_attr(pr=1)
        fwp.set_attr(eta_s=1)
        steam_generator.set_attr(pr=1)

        # connection parameters
        fs_in.set_attr(m=10, p=120, T=600, fluid={'water': 1})
        cond.set_attr(T=self.Tamb, x=0)

        # solve network
        self.nw.solve('design')
        convergence_check(self.nw.lin_dep)

    def test_exergy_analysis_perfect_cycle(self):
        """Test exergy analysis in the perfect clausius rankine cycle."""
        ean = ExergyAnalysis(self.nw,
                             E_P=[self.power],
                             E_F=[self.heat],
                             internal_busses=[self.fwp_power])
        ean.analyse(pamb=self.pamb, Tamb=self.Tamb)
        msg = ('Exergy destruction of this network must be 0 (smaller than ' +
               str(err**0.5) + ') for this test but is ' +
               str(round(abs(ean.network_data.E_D), 4)) + ' .')
        assert abs(ean.network_data.E_D) <= err**0.5, msg

        msg = ('Exergy efficiency of this network must be 1 for this test but '
               'is ' + str(round(ean.network_data.epsilon, 4)) + ' .')
        assert round(ean.network_data.epsilon, 4) == 1, msg

        exergy_balance = (ean.network_data.E_F - ean.network_data.E_P -
                          ean.network_data.E_L - ean.network_data.E_D)
        msg = ('Exergy balance must be closed (residual value smaller than ' +
               str(err**0.5) + ') for this test but is ' +
               str(round(abs(exergy_balance), 4)) + ' .')
        assert abs(exergy_balance) <= err**0.5, msg

        msg = (
            'Fuel exergy and product exergy must be identical for this test. '
            'Fuel exergy value: ' + str(round(ean.network_data.E_F, 4)) +
            '. Product exergy value: ' + str(round(ean.network_data.E_P, 4)) +
            '.')
        delta = round(abs(ean.network_data.E_F - ean.network_data.E_P), 4)
        assert delta < err**0.5, msg

    def test_exergy_analysis_plotting_data(self):
        """Test exergy analysis plotting."""
        self.nw.get_comp('steam generator').set_attr(pr=0.9)
        self.nw.get_comp('turbine').set_attr(eta_s=0.9)
        self.nw.get_comp('feed water pump turbine').set_attr(eta_s=0.85)
        self.nw.get_comp('pump').set_attr(eta_s=0.75)
        self.nw.get_conn('cond').set_attr(T=self.Tamb + 3)

        # specify efficiency values for the internal bus and power bus
        self.nw.del_busses(self.fwp_power, self.power)

        self.fwp_power = Bus('feed water pump power', P=0)
        self.fwp_power.add_comps(
            {
                'comp': self.nw.get_comp('feed water pump turbine'),
                'char': 0.99
            }, {
                'comp': self.nw.get_comp('pump'),
                'char': 0.98,
                'base': 'bus'
            })
        self.power = Bus('power_output')
        self.power.add_comps({
            'comp': self.nw.get_comp('turbine'),
            'char': 0.98
        })

        self.nw.add_busses(self.fwp_power, self.power)

        # solve network
        self.nw.solve('design')
        convergence_check(self.nw.lin_dep)
        ean = ExergyAnalysis(self.nw,
                             E_P=[self.power],
                             E_F=[self.heat],
                             internal_busses=[self.fwp_power])
        ean.analyse(pamb=self.pamb, Tamb=self.Tamb)

        exergy_balance = (ean.network_data.E_F - ean.network_data.E_P -
                          ean.network_data.E_L - ean.network_data.E_D)
        msg = ('Exergy balance must be closed (residual value smaller than ' +
               str(err**0.5) + ') for this test but is ' +
               str(round(abs(exergy_balance), 4)) + ' .')
        assert abs(exergy_balance) <= err**0.5, msg

        nodes = [
            'E_F', 'steam generator', 'splitter 1', 'feed water pump turbine',
            'turbine', 'merge 1', 'condenser', 'pump', 'E_D', 'E_P'
        ]

        links, nodes = ean.generate_plotly_sankey_input(node_order=nodes)
        # checksum for targets and source
        checksum = sum(links['target'] + links['source'])
        msg = ('The checksum of all target and source values in the link lists'
               'must be 148, but is ' + str(checksum) + '.')
        assert 148 == checksum, msg

    def test_exergy_analysis_violated_balance(self):
        """Test exergy analysis with violated balance."""
        # specify efficiency values for the internal bus
        self.nw.del_busses(self.fwp_power)
        self.fwp_power = Bus('feed water pump power', P=0)
        self.fwp_power.add_comps(
            {
                'comp': self.nw.get_comp('feed water pump turbine'),
                'char': 0.99
            }, {
                'comp': self.nw.get_comp('pump'),
                'char': 0.98,
                'base': 'bus'
            })
        self.nw.add_busses(self.fwp_power)
        self.nw.solve('design')
        convergence_check(self.nw.lin_dep)
        # miss out on internal bus in exergy_analysis
        ean = ExergyAnalysis(self.nw, E_P=[self.power], E_F=[self.heat])
        ean.analyse(pamb=self.pamb, Tamb=self.Tamb)

        exergy_balance = (ean.network_data.E_F - ean.network_data.E_P -
                          ean.network_data.E_L - ean.network_data.E_D)
        msg = ('Exergy balance must be violated for this test (larger than ' +
               str(err**0.5) + ') but is ' +
               str(round(abs(exergy_balance), 4)) + ' .')
        assert abs(exergy_balance) > err**0.5, msg

    def test_exergy_analysis_bus_conversion(self):
        """Test exergy analysis bus conversion factors."""
        # specify efficiency values for the internal bus
        self.nw.del_busses(self.fwp_power)
        self.fwp_power = Bus('feed water pump power', P=0)
        self.fwp_power.add_comps(
            {
                'comp': self.nw.get_comp('feed water pump turbine'),
                'char': 0.99
            }, {
                'comp': self.nw.get_comp('pump'),
                'char': 0.98,
                'base': 'bus'
            })
        self.nw.add_busses(self.fwp_power)
        self.nw.solve('design')
        convergence_check(self.nw.lin_dep)
        # no exergy losses in this case
        ean = ExergyAnalysis(self.nw,
                             E_P=[self.power],
                             E_F=[self.heat],
                             internal_busses=[self.fwp_power])
        ean.analyse(pamb=self.pamb, Tamb=self.Tamb)

        label = 'pump'
        eps = ean.bus_data.loc[label, 'epsilon']
        msg = ('Pump exergy efficiency must be 0.98 but is ' +
               str(round(eps, 4)) + ' .')
        assert round(eps, 4) == 0.98, msg

        label = 'feed water pump turbine'
        eps = ean.bus_data.loc[label, 'epsilon']
        msg = (
            'Feed water pump turbine exergy efficiency must be 0.99 but is ' +
            str(round(eps, 4)) + ' .')
        assert round(eps, 4) == 0.99, msg

    def test_exergy_analysis_missing_E_F_E_P_information(self):
        """Test exergy analysis errors with missing information."""
        with raises(TESPyNetworkError):
            ExergyAnalysis(self.nw, E_P=[self.power], E_F=[])

        with raises(TESPyNetworkError):
            ExergyAnalysis(self.nw, E_P=[], E_F=[self.heat])

    def test_exergy_analysis_component_on_two_busses(self):
        """Test exergy analysis errors with components on more than one bus."""
        with raises(TESPyNetworkError):
            ean = ExergyAnalysis(self.nw,
                                 E_P=[self.power],
                                 E_F=[self.heat, self.power])
            ean.analyse(pamb=self.pamb, Tamb=self.Tamb)
class TestClausiusRankine:
    def setup(self):
        """Set up clausis rankine cycle with turbine driven feed water pump."""
        self.Tamb = 20
        self.pamb = 1
        fluids = ['water']
        self.nw = Network(fluids=fluids)
        self.nw.set_attr(p_unit='bar', T_unit='C', h_unit='kJ / kg')

        # create components
        splitter1 = Splitter('splitter 1')
        merge1 = Merge('merge 1')
        turb = Turbine('turbine')
        fwp_turb = Turbine('feed water pump turbine')
        condenser = HeatExchangerSimple('condenser')
        fwp = Pump('pump')
        steam_generator = HeatExchangerSimple('steam generator')
        cycle_close = CycleCloser('cycle closer')

        # create busses
        # power output bus
        self.power = Bus('power_output')
        self.power.add_comps({'comp': turb, 'char': 1})
        # turbine driven feed water pump internal bus
        self.fwp_power = Bus('feed water pump power', P=0)
        self.fwp_power.add_comps({
            'comp': fwp_turb,
            'char': 1
        }, {
            'comp': fwp,
            'char': 1,
            'base': 'bus'
        })
        # heat input bus
        self.heat = Bus('heat_input')
        self.heat.add_comps({'comp': steam_generator, 'base': 'bus'})
        self.nw.add_busses(self.power, self.fwp_power, self.heat)

        # create connections
        fs_in = Connection(cycle_close, 'out1', splitter1, 'in1', label='fs')
        fs_fwpt = Connection(splitter1, 'out1', fwp_turb, 'in1')
        fs_t = Connection(splitter1, 'out2', turb, 'in1')
        fwpt_ws = Connection(fwp_turb, 'out1', merge1, 'in1')
        t_ws = Connection(turb, 'out1', merge1, 'in2')
        ws = Connection(merge1, 'out1', condenser, 'in1')
        cond = Connection(condenser, 'out1', fwp, 'in1', label='cond')
        fw = Connection(fwp, 'out1', steam_generator, 'in1', label='fw')
        fs_out = Connection(steam_generator, 'out1', cycle_close, 'in1')
        self.nw.add_conns(fs_in, fs_fwpt, fs_t, fwpt_ws, t_ws, ws, cond, fw,
                          fs_out)

        # component parameters
        turb.set_attr(eta_s=1)
        fwp_turb.set_attr(eta_s=1)
        condenser.set_attr(pr=1)
        fwp.set_attr(eta_s=1)
        steam_generator.set_attr(pr=1)

        # connection parameters
        fs_in.set_attr(m=10, p=120, T=600, fluid={'water': 1})
        cond.set_attr(T=self.Tamb, x=0)

        # solve network
        self.nw.solve('design')
        convergence_check(self.nw.lin_dep)

    def test_entropy_perfect_cycle(self):
        """Test entropy values in the perfect clausius rankine cycle."""
        labels = [
            'turbine', 'feed water pump turbine', 'condenser',
            'steam generator', 'pump'
        ]
        for label in labels:
            cp = self.nw.get_comp(label)
            msg = (
                'Entropy production due to irreversibility must be 0 for all '
                'components in this test but is ' + str(round(cp.S_irr, 4)) +
                ' at component ' + label + ' of type ' + cp.component() + '.')
            assert round(cp.S_irr, 4) == 0, msg
        sg = self.nw.get_comp('steam generator')
        cd = self.nw.get_comp('condenser')
        msg = (
            'Value of entropy production due to heat input at steam generator '
            '(S_Q=' + str(round(sg.S_Q, 4)) + ') must equal the negative '
            'value of entropy reduction in condenser (S_Q=' +
            str(round(cd.S_Q, 4)) + ').')
        assert round(sg.S_Q, 4) == -round(cd.S_Q, 4), msg
示例#5
0
class TestClausiusRankine:
    def setup(self):
        """Set up clausis rankine cycle with turbine driven feed water pump."""
        self.Tamb = 20
        self.pamb = 1
        fluids = ['water']
        self.nw = Network(fluids=fluids)
        self.nw.set_attr(p_unit='bar', T_unit='C', h_unit='kJ / kg')

        # create components
        splitter1 = Splitter('splitter 1')
        merge1 = Merge('merge 1')
        turb = Turbine('turbine')
        fwp_turb = Turbine('feed water pump turbine')
        condenser = HeatExchangerSimple('condenser')
        fwp = Pump('pump')
        steam_generator = HeatExchangerSimple('steam generator')
        cycle_close = CycleCloser('cycle closer')

        # create busses
        # power output bus
        self.power = Bus('power_output')
        self.power.add_comps({'comp': turb, 'char': 1})
        # turbine driven feed water pump internal bus
        self.fwp_power = Bus('feed water pump power', P=0)
        self.fwp_power.add_comps({
            'comp': fwp_turb,
            'char': 1
        }, {
            'comp': fwp,
            'char': 1,
            'base': 'bus'
        })
        # heat input bus
        self.heat = Bus('heat_input')
        self.heat.add_comps({'comp': steam_generator, 'base': 'bus'})
        self.nw.add_busses(self.power, self.fwp_power, self.heat)

        # create connections
        fs_in = Connection(cycle_close, 'out1', splitter1, 'in1', label='fs')
        fs_fwpt = Connection(splitter1, 'out1', fwp_turb, 'in1')
        fs_t = Connection(splitter1, 'out2', turb, 'in1')
        fwpt_ws = Connection(fwp_turb, 'out1', merge1, 'in1')
        t_ws = Connection(turb, 'out1', merge1, 'in2')
        ws = Connection(merge1, 'out1', condenser, 'in1')
        cond = Connection(condenser, 'out1', fwp, 'in1', label='cond')
        fw = Connection(fwp, 'out1', steam_generator, 'in1', label='fw')
        fs_out = Connection(steam_generator, 'out1', cycle_close, 'in1')
        self.nw.add_conns(fs_in, fs_fwpt, fs_t, fwpt_ws, t_ws, ws, cond, fw,
                          fs_out)

        # component parameters
        turb.set_attr(eta_s=1)
        fwp_turb.set_attr(eta_s=1)
        condenser.set_attr(pr=1)
        fwp.set_attr(eta_s=1)
        steam_generator.set_attr(pr=1)

        # connection parameters
        fs_in.set_attr(m=10, p=120, T=600, fluid={'water': 1})
        cond.set_attr(T=self.Tamb, x=0)

        # solve network
        self.nw.solve('design')
        convergence_check(self.nw.lin_dep)

    def test_exergy_analysis_perfect_cycle(self):
        """Test exergy analysis in the perfect clausius rankine cycle."""
        self.nw.exergy_analysis(self.pamb,
                                self.Tamb,
                                E_P=[self.power],
                                E_F=[self.heat],
                                internal_busses=[self.fwp_power])
        msg = ('Exergy destruction of this network must be 0 (smaller than ' +
               str(err**0.5) + ') for this test but is ' +
               str(round(abs(self.nw.E_D), 4)) + ' .')
        assert abs(self.nw.E_D) <= err**0.5, msg

        msg = ('Exergy efficiency of this network must be 1 for this test but '
               'is ' + str(round(self.nw.epsilon, 4)) + ' .')
        assert round(self.nw.epsilon, 4) == 1, msg

        exergy_balance = self.nw.E_F - self.nw.E_P - self.nw.E_L - self.nw.E_D
        msg = ('Exergy balance must be closed (residual value smaller than ' +
               str(err**0.5) + ') for this test but is ' +
               str(round(abs(exergy_balance), 4)) + ' .')
        assert abs(exergy_balance) <= err**0.5, msg

        msg = (
            'Fuel exergy and product exergy must be identical for this test. '
            'Fuel exergy value: ' + str(round(self.nw.E_F, 4)) +
            '. Product exergy value: ' + str(round(self.nw.E_P, 4)) + '.')
        assert round(abs(self.nw.E_F - self.nw.E_P), 4) < err**0.5, msg

    def test_entropy_perfect_cycle(self):
        """Test entropy values in the perfect clausius rankine cycle."""
        labels = [
            'turbine', 'feed water pump turbine', 'condenser',
            'steam generator', 'pump'
        ]
        for label in labels:
            cp = self.nw.get_comp(label)
            msg = (
                'Entropy production due to irreversibility must be 0 for all '
                'components in this test but is ' + str(round(cp.S_irr, 4)) +
                ' at component ' + label + ' of type ' + cp.component() + '.')
            assert round(cp.S_irr, 4) == 0, msg
        sg = self.nw.get_comp('steam generator')
        cd = self.nw.get_comp('condenser')
        msg = (
            'Value of entropy production due to heat input at steam generator '
            '(S_Q=' + str(round(sg.S_Q, 4)) + ') must equal the negative '
            'value of entropy reduction in condenser (S_Q=' +
            str(round(cd.S_Q, 4)) + ').')
        assert round(sg.S_Q, 4) == -round(cd.S_Q, 4), msg

    def test_exergy_analysis_violated_balance(self):
        """Test exergy analysis with violated balance."""
        # specify efficiency values for the internal bus
        self.nw.del_busses(self.fwp_power)
        self.fwp_power = Bus('feed water pump power', P=0)
        self.fwp_power.add_comps(
            {
                'comp': self.nw.get_comp('feed water pump turbine'),
                'char': 0.99
            }, {
                'comp': self.nw.get_comp('pump'),
                'char': 0.98,
                'base': 'bus'
            })
        self.nw.add_busses(self.fwp_power)
        self.nw.solve('design')
        convergence_check(self.nw.lin_dep)
        # miss out on internal bus in exergy_analysis
        self.nw.exergy_analysis(self.pamb,
                                self.Tamb,
                                E_P=[self.power],
                                E_F=[self.heat])

        exergy_balance = self.nw.E_F - self.nw.E_P - self.nw.E_L - self.nw.E_D
        msg = ('Exergy balance must be violated for this test (larger than ' +
               str(err**0.5) + ') but is ' +
               str(round(abs(exergy_balance), 4)) + ' .')
        assert abs(exergy_balance) > err**0.5, msg

    def test_exergy_analysis_bus_conversion(self):
        """Test exergy analysis bus conversion factors."""
        # specify efficiency values for the internal bus
        self.nw.del_busses(self.fwp_power)
        self.fwp_power = Bus('feed water pump power', P=0)
        self.fwp_power.add_comps(
            {
                'comp': self.nw.get_comp('feed water pump turbine'),
                'char': 0.99
            }, {
                'comp': self.nw.get_comp('pump'),
                'char': 0.98,
                'base': 'bus'
            })
        self.nw.add_busses(self.fwp_power)
        self.nw.solve('design')
        convergence_check(self.nw.lin_dep)
        # no exergy losses in this case
        self.nw.exergy_analysis(self.pamb,
                                self.Tamb,
                                E_P=[self.power],
                                E_F=[self.heat],
                                internal_busses=[self.fwp_power])
        label = 'pump on bus feed water pump power'
        eps = self.nw.component_exergy_data.loc[label, 'epsilon']
        msg = ('Pump exergy efficiency must be 0.98 but is ' +
               str(round(eps, 4)) + ' .')
        assert round(eps, 4) == 0.98, msg

        label = 'feed water pump turbine on bus feed water pump power'
        eps = self.nw.component_exergy_data.loc[label, 'epsilon']
        eps = self.nw.component_exergy_data.loc[label, 'epsilon']
        msg = (
            'Feed water pump turbine exergy efficiency must be 0.99 but is ' +
            str(round(eps, 4)) + ' .')
        assert round(eps, 4) == 0.99, msg

    def test_exergy_analysis_missing_E_F_E_P_information(self):
        """Test exergy analysis errors with missing information."""
        with raises(TESPyNetworkError):
            self.nw.exergy_analysis(self.pamb,
                                    self.Tamb,
                                    E_P=[self.power],
                                    E_F=[])

        with raises(TESPyNetworkError):
            self.nw.exergy_analysis(self.pamb,
                                    self.Tamb,
                                    E_P=[],
                                    E_F=[self.heat])

    def test_exergy_analysis_component_on_two_busses(self):
        """Test exergy analysis errors with components on more than one bus."""
        with raises(TESPyNetworkError):
            self.nw.exergy_analysis(self.pamb,
                                    self.Tamb,
                                    E_P=[self.power],
                                    E_F=[self.heat, self.power])
示例#6
0
class TestBusses:
    def setup(self):
        """Set up the model."""
        # %% network setup
        fluid_list = ['Ar', 'N2', 'O2', 'CO2', 'CH4', 'H2O']
        self.nw = Network(fluids=fluid_list,
                          p_unit='bar',
                          T_unit='C',
                          p_range=[0.5, 20])

        # %% components
        amb = Source('ambient')
        sf = Source('fuel')
        cc = CombustionChamber('combustion')
        cp = Compressor('compressor')
        gt = Turbine('turbine')
        fg = Sink('flue gas outlet')

        # %% connections
        amb_cp = Connection(amb, 'out1', cp, 'in1', label='ambient air flow')
        cp_cc = Connection(cp, 'out1', cc, 'in1')
        sf_cc = Connection(sf, 'out1', cc, 'in2')
        cc_gt = Connection(cc, 'out1', gt, 'in1')
        gt_fg = Connection(gt, 'out1', fg, 'in1')

        self.nw.add_conns(amb_cp, cp_cc, sf_cc, cc_gt, gt_fg)

        # %% component parameters
        cc.set_attr(lamb=3)
        cp.set_attr(eta_s=0.9, pr=15)
        gt.set_attr(eta_s=0.9)

        # %% connection parameters
        amb_cp.set_attr(T=20,
                        p=1,
                        m=100,
                        fluid={
                            'Ar': 0.0129,
                            'N2': 0.7553,
                            'H2O': 0,
                            'CH4': 0,
                            'CO2': 0.0004,
                            'O2': 0.2314
                        })
        sf_cc.set_attr(T=20,
                       fluid={
                           'CO2': 0.04,
                           'Ar': 0,
                           'N2': 0,
                           'O2': 0,
                           'H2O': 0,
                           'CH4': 0.96
                       })
        gt_fg.set_attr(p=1)

        # motor efficiency
        x = np.array([
            0, 0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55,
            0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 1, 1.05, 1.1, 1.15,
            1.2, 10
        ])
        y = np.array([
            0.01, 0.3148, 0.5346, 0.6843, 0.7835, 0.8477, 0.8885, 0.9145,
            0.9318, 0.9443, 0.9546, 0.9638, 0.9724, 0.9806, 0.9878, 0.9938,
            0.9982, 0.999, 0.9995, 0.9999, 1, 0.9977, 0.9947, 0.9909, 0.9853,
            0.9644
        ]) * 0.975
        self.motor_bus_based = CharLine(x=x, y=y)
        self.motor_comp_based = CharLine(x=x, y=1 / y)

        # generator efficiency
        x = np.array([
            0.100, 0.345, 0.359, 0.383, 0.410, 0.432, 0.451, 0.504, 0.541,
            0.600, 0.684, 0.805, 1.000, 1.700, 10
        ])
        y = np.array([
            0.976, 0.989, 0.990, 0.991, 0.992, 0.993, 0.994, 0.995, 0.996,
            0.997, 0.998, 0.999, 1.000, 0.999, 0.99
        ]) * 0.975
        self.generator = CharLine(x=x, y=y)

        power_bus_total = Bus('total power output')
        power_bus_total.add_comps(
            {
                'comp': cp,
                'char': self.motor_bus_based,
                'base': 'bus'
            }, {
                'comp': gt,
                'char': self.generator
            })

        thermal_input = Bus('thermal input')
        thermal_input.add_comps({'comp': cc})

        compressor_power_comp = Bus('compressor power input')
        compressor_power_comp.add_comps({
            'comp': cp,
            'char': self.motor_comp_based
        })

        compressor_power_bus = Bus('compressor power input bus based')
        compressor_power_bus.add_comps({
            'comp': cp,
            'char': self.motor_bus_based,
            'base': 'bus'
        })

        self.nw.add_busses(power_bus_total, thermal_input,
                           compressor_power_comp, compressor_power_bus)

        # %% solving
        self.nw.solve('design')
        self.nw.save('tmp')

    def test_model(self):
        """Test the bus functionalities in a gas turbine model."""
        tpo = self.nw.busses['total power output']
        ti = self.nw.busses['thermal input']
        cpi = self.nw.busses['compressor power input']
        cpibb = self.nw.busses['compressor power input bus based']

        cp = self.nw.get_comp('compressor')
        gt = self.nw.get_comp('turbine')
        cc = self.nw.get_comp('combustion')

        # test results of design case

        eta_cpi = round(1 / cp.calc_bus_efficiency(cpi), 6)
        eta_cp_tpo = round(cp.calc_bus_efficiency(tpo), 6)
        msg = ('The efficiency value of the compressor on the bus ' +
               tpo.label + ' (' + str(eta_cp_tpo) +
               ') must be identical to the efficiency '
               'on the bus ' + cpi.label + ' (' + str(eta_cpi) + ').')
        assert eta_cp_tpo == eta_cpi, msg

        P_cp_tpo = cp.calc_bus_value(tpo)
        eta_cp_tpo = cp.calc_bus_efficiency(tpo)
        P_cp = round(P_cp_tpo * eta_cp_tpo, 0)
        msg = ('The compressor power must be ' + str(round(cp.P.val, 0)) +
               ' on '
               'the bus ' + tpo.label + ' but is ' + str(P_cp) + ').')
        assert round(cp.P.val, 0) == P_cp, msg

        P_cp_tpo = round(
            cp.calc_bus_value(tpo) * cp.calc_bus_efficiency(tpo), 0)
        P_cp_cpi = round(
            cp.calc_bus_value(cpi) / cp.calc_bus_efficiency(cpi), 0)
        P_cp_cpibb = round(
            cp.calc_bus_value(cpibb) * cp.calc_bus_efficiency(cpibb), 0)
        msg = (
            'The busses\' component power value for the compressor on bus ' +
            tpo.label + ' (' + str(P_cp_tpo) + ') must be equal to the '
            'component power on all other busses. Bus ' + cpi.label + ' (' +
            str(P_cp_cpi) + ') and bus ' + cpibb.label + ' (' +
            str(P_cp_cpibb) + ').')
        assert P_cp_tpo == P_cp_cpi and P_cp_tpo == P_cp_cpibb, msg

        eta_gt_tpo = gt.calc_bus_efficiency(tpo)
        msg = ('The efficiency value of the turbine on the bus ' + tpo.label +
               ' (' + str(eta_gt_tpo) + ') must be equal to 0.975.')
        assert eta_gt_tpo == 0.975, msg

        eta_ti = cc.calc_bus_efficiency(ti)
        msg = ('The efficiency value of the combustion chamber on the bus ' +
               ti.label + ' (' + str(eta_ti) + ') must be equal to 1.0.')
        assert eta_ti == 1.0, msg

        # test partload for bus functions
        # first test in identical conditions

        self.nw.get_conn('ambient air flow').set_attr(m=None)
        P_design = cpibb.P.val
        cpibb.set_attr(P=P_design)
        self.nw.solve('offdesign', design_path='tmp')

        eta_cpi = round(1 / cp.calc_bus_efficiency(cpi), 6)
        eta_cp_tpo = round(cp.calc_bus_efficiency(tpo), 6)
        msg = ('The efficiency value of the compressor on the bus ' +
               tpo.label + ' (' + str(eta_cp_tpo) +
               ') must be identical to the efficiency '
               'on the bus ' + cpi.label + ' (' + str(eta_cpi) + ').')
        assert eta_cp_tpo == eta_cpi, msg

        eta_gt_tpo = gt.calc_bus_efficiency(tpo)
        msg = ('The efficiency value of the turbine on the bus ' + tpo.label +
               ' (' + str(eta_gt_tpo) + ') must be equal to 0.975.')
        assert eta_gt_tpo == 0.975, msg

        P_cp_tpo = round(
            cp.calc_bus_value(tpo) * cp.calc_bus_efficiency(tpo), 0)
        P_cp_cpi = round(
            cp.calc_bus_value(cpi) / cp.calc_bus_efficiency(cpi), 0)
        P_cp_cpibb = round(
            cp.calc_bus_value(cpibb) * cp.calc_bus_efficiency(cpibb), 0)
        msg = (
            'The busses\' component power value for the compressor on bus ' +
            tpo.label + ' (' + str(P_cp_tpo) + ') must be equal to the '
            'component power on all other busses. Bus ' + cpi.label + ' (' +
            str(P_cp_cpi) + ') and bus ' + cpibb.label + ' (' +
            str(P_cp_cpibb) + ').')
        assert P_cp_tpo == P_cp_cpi and P_cp_tpo == P_cp_cpibb, msg

        # 60 % load
        load = 0.6
        cpibb.set_attr(P=P_design * load)
        self.nw.solve('offdesign', design_path='tmp')

        eta_cp_tpo = round(cp.calc_bus_efficiency(tpo), 6)
        eta_cp_char = self.motor_bus_based.evaluate(load)
        msg = ('The efficiency value of the compressor on the bus ' +
               tpo.label + ' (' + str(eta_cp_tpo) +
               ') must be identical to the efficiency '
               'on the characteristic line (' + str(eta_cp_char) + ').')
        assert eta_cp_tpo == eta_cp_char, msg

        load_frac = round(
            cp.calc_bus_value(tpo) / tpo.comps.loc[cp, 'P_ref'], 6)
        msg = ('The load fraction value of the compressor on the bus ' +
               tpo.label + ' (' + str(load_frac) +
               ') must be identical to the '
               'load fraction value on the bus ' + cpibb.label + ' (' +
               str(load) + ').')
        assert load == load_frac, msg

        eta_cpi = round(1 / cp.calc_bus_efficiency(cpi), 6)
        eta_cp_tpo = round(cp.calc_bus_efficiency(tpo), 6)
        msg = ('The efficiency value of the compressor on the bus ' +
               tpo.label + ' (' + str(eta_cp_tpo) +
               ') must be higher than the efficiency '
               'on the bus ' + cpi.label + ' (' + str(eta_cpi) + ').')
        assert eta_cp_tpo > eta_cpi, msg

        P_cp_tpo = round(
            cp.calc_bus_value(tpo) * cp.calc_bus_efficiency(tpo), 0)
        P_cp_cpi = round(
            cp.calc_bus_value(cpi) / cp.calc_bus_efficiency(cpi), 0)
        P_cp_cpibb = round(
            cp.calc_bus_value(cpibb) * cp.calc_bus_efficiency(cpibb), 0)
        msg = (
            'The busses\' component power value for the compressor on bus ' +
            tpo.label + ' (' + str(P_cp_tpo) + ') must be equal to the '
            'component power on all other busses. Bus ' + cpi.label + ' (' +
            str(P_cp_cpi) + ') and bus ' + cpibb.label + ' (' +
            str(P_cp_cpibb) + ').')
        assert P_cp_tpo == P_cp_cpi and P_cp_tpo == P_cp_cpibb, msg

        shutil.rmtree('tmp', ignore_errors=True)