Skip to content
Snippets Groups Projects
test_esipp_problem.py 269 KiB
Newer Older
  • Learn to ignore specific revisions
  •             },
            )
            
            # A
            node_A = generate_pseudo_unique_key(mynet.nodes())
            mynet.add_source_sink_node(node_key=node_A, base_flow={(q, 0): 0.0, (q, 1): 1.0})
    
            # B
            node_B = generate_pseudo_unique_key(mynet.nodes())
            mynet.add_source_sink_node(node_key=node_B, base_flow={(q, 0): 1.0, (q, 1): -0.5})
    
            # C
            node_C = generate_pseudo_unique_key(mynet.nodes())
            mynet.add_source_sink_node(node_key=node_C, base_flow={(q, 0): 0.0, (q, 1): 0.5})
    
            # D
            node_D = generate_pseudo_unique_key(mynet.nodes())
            mynet.add_source_sink_node(node_key=node_D, base_flow={(q, 0): 0.5, (q, 1): -0.25})
    
    
            # *********************************************************************
            # *********************************************************************
            
    
            # add arcs
            # IA
            mynet.add_preexisting_directed_arc(
                node_key_a=imp_node_key,
                node_key_b=node_A,
                efficiency=None,
                static_loss=None,
                capacity=1.5,
                capacity_is_instantaneous=False,
            )
    
            # IC
            mynet.add_preexisting_directed_arc(
                node_key_a=imp_node_key,
                node_key_b=node_C,
                efficiency=None,
                static_loss=None,
                capacity=1.5,
                capacity_is_instantaneous=False,
            )
    
            # AB
            efficiency_AB = {
                (q, 0): 1.00,
                (q, 1): 0.85,
            }
            efficiency_BA = {
                (q, 0): 0.95,
                (q, 1): 0.80,
            }
            static_loss_AB = {
                (0, q, 0): 0.20,
                (0, q, 1): 0.25,
                (1, q, 0): 0.25,
                (1, q, 1): 0.30,
                (2, q, 0): 0.30,
                (2, q, 1): 0.35,
            }
    
            arcs_AB = Arcs(
                name="AB",
                efficiency=efficiency_AB,
                efficiency_reverse=efficiency_BA,
                static_loss=static_loss_AB,
                capacity=(0.85, 1.5, 2.5),
                minimum_cost=(1, 2, 3),
                specific_capacity_cost=0,
                capacity_is_instantaneous=False,
                validate=True,
            )
    
            arc_key_AB = mynet.add_undirected_arc(
                node_key_a=node_A, node_key_b=node_B, arcs=arcs_AB
            )
    
            # CD
            efficiency_CD = {
                (q, 0): 1.00,
                (q, 1): 0.85,
            }
            efficiency_DC = {(q, 0): 0.95, (q, 1): 0.80}
            static_loss_CD = {
                (0, q, 0): 0.010,
                (0, q, 1): 0.015,
                (1, q, 0): 0.015,
                (1, q, 1): 0.020,
                (2, q, 0): 0.020,
                (2, q, 1): 0.025,
            }
            arcs_CD = Arcs(
                name="CD",
                efficiency=efficiency_CD,
                efficiency_reverse=efficiency_DC,
                static_loss=static_loss_CD,
                capacity=(0.85, 1.5, 2.5),
                minimum_cost=(1, 2, 3),
                specific_capacity_cost=0,
                capacity_is_instantaneous=False,
                validate=True,
            )
            arc_key_CD = mynet.add_undirected_arc(
                node_key_a=node_C, node_key_b=node_D, arcs=arcs_CD
            )
    
            # arc groups
            arc_groups_dict = {}
    
            # identify node types
            mynet.identify_node_types()
    
            # solver settings
            solver_options = {}
            solver_options["relative_mip_gap"] = 0
            solver_options["absolute_mip_gap"] = 1e-4
    
            # no sos, regular time intervals
            
            for static_losses_mode in InfrastructurePlanningProblem.STATIC_LOSS_MODES:
                # reset decisions if necessary
                if True in mynet.edges[(node_A, node_B, arc_key_AB)][Network.KEY_ARC_TECH].options_selected:
                    mynet.edges[(node_A, node_B, arc_key_AB)][
                        Network.KEY_ARC_TECH].options_selected[
                            mynet.edges[(node_A, node_B, arc_key_AB)][
                                Network.KEY_ARC_TECH].options_selected.index(True)
                            ] = False
                if True in mynet.edges[(node_C, node_D, arc_key_CD)][Network.KEY_ARC_TECH].options_selected:
                    mynet.edges[(node_C, node_D, arc_key_CD)][
                        Network.KEY_ARC_TECH].options_selected[
                            mynet.edges[(node_C, node_D, arc_key_CD)][
                                Network.KEY_ARC_TECH].options_selected.index(True)
                            ] = False
                
                ipp = self.build_solve_ipp(
                    solver_options=solver_options,
                    perform_analysis=False,
                    plot_results=False,  # True,
                    print_solver_output=False,
                    time_frame=tf,
                    networks={"mynet": mynet},
                    static_losses_mode=static_losses_mode,
                    arc_groups_dict=arc_groups_dict,
                    max_number_parallel_arcs={}
                )
                
                # overview
    
                (imports_qpk, 
                 exports_qpk, 
                 balance_qpk, 
                 import_costs_qpk, 
                 export_revenue_qpk, 
                 ncf_qpk, 
                 aggregate_static_demand_qpk,
                 aggregate_static_supply_qpk,
                 aggregate_static_balance_qpk) = statistics(ipp)
    
        
                capex_ind = 3
                capex_group = 4
        
                imp_ind = 2.912
                imp_group = 2.9210000000000003
        
                sdncf_ind = -7.72035753459824
        
                sdnext_ind = 0
        
                obj_ind = sdnext_ind + sdncf_ind - capex_ind
        
                losses_ind = sum(
                    static_loss_AB[(1, q, k)] + static_loss_CD[(0, q, k)]
                    for k in range(tf.number_time_intervals(q))
                )
                losses_group = sum(
                    static_loss_AB[(1, q, k)] + static_loss_CD[(1, q, k)]
                    for k in range(tf.number_time_intervals(q))
                )
    
                losses_model = sum(
                    pyo.value(
                        ipp.instance.var_w_glljqk[("mynet", node_A, node_B, arc_key_AB, q, k)]
                    )
                    + pyo.value(
                        ipp.instance.var_w_glljqk[("mynet", node_C, node_D, arc_key_CD, q, k)]
                    )
                    for k in range(tf.number_time_intervals(q))
                )
        
                assert capex_group > capex_ind
                # # assert math.isclose(losses_group, losses_ind, abs_tol=1e-3)
                assert losses_group > losses_ind
                assert imp_group > imp_ind
        
                # at least one arc has to be installed
                assert (
                    True
                    in ipp.networks["mynet"]
                    .edges[(node_A, node_B, arc_key_AB)][Network.KEY_ARC_TECH]
                    .options_selected
                    or True
                    in ipp.networks["mynet"]
                    .edges[(node_C, node_D, arc_key_CD)][Network.KEY_ARC_TECH]
                    .options_selected
                )
        
                # the capex have to be lower than with a group of arcs
                abs_tol = 1e-3
                assert math.isclose(
                    pyo.value(ipp.instance.var_capex), capex_ind, abs_tol=abs_tol
                )
        
                # there should be no exports
    
                abs_tol = 1e-3
                exports_qp = sum(exports_qpk[(q, 0, k)] for k in tf.time_intervals[q])
                assert math.isclose(exports_qp, 0, abs_tol=abs_tol)
    
        
                # the imports should be lower than with a group of arcs
                abs_tol = 1e-3
    
                imports_qp = sum(imports_qpk[qpk] for qpk in tf.qpk() if qpk[1] == 0)
                assert math.isclose(imports_qp, imp_ind, abs_tol=abs_tol)
    
        
                # the operating results should be lower than with an individual arc
                abs_tol = 1e-3
                assert math.isclose(
                    pyo.value(ipp.instance.var_sdncf_q[q]), sdncf_ind, abs_tol=abs_tol
                )
        
                # the externalities should be zero
                abs_tol = 1e-3
                assert math.isclose(pyo.value(ipp.instance.var_sdext_q[q]), 0, abs_tol=abs_tol)
        
                # the objective function should be -6.3639758220728595-1.5
                abs_tol = 1e-3
                assert math.isclose(pyo.value(ipp.instance.obj_f), obj_ind, abs_tol=abs_tol)
        
                # the imports should be greater than or equal to the losses for all arx
                assert math.isclose(losses_model, losses_ind, abs_tol=abs_tol)
    
                
        # *************************************************************************
        # *************************************************************************
            
        def test_direct_imp_exp_network(self):
            
            # time frame
            q = 0
            tf = EconomicTimeFrame(
                discount_rate=3.5/100,
                reporting_periods={q: (0,1)},
                reporting_period_durations={q: (365 * 24 * 3600,365 * 24 * 3600)},
                time_intervals={q: (0,1)},
                time_interval_durations={q: (1,1)},
            )    
            
            # 4 nodes: one import, one export, two supply/demand nodes
            mynet = Network()
        
            # import node
    
            imp_prices = {
                qpk: ResourcePrice(
                    prices=1.5,
                    volumes=None,
                )
                for qpk in tf.qpk()
                }
            mynet.add_import_node(
                node_key=imp_node_key,
                prices=imp_prices
            )
        
            # export node
    
            exp_prices = {
                qpk: ResourcePrice(
                    prices=0.5,
                    volumes=None,
                )
                for qpk in tf.qpk()
                }
            mynet.add_export_node(
                node_key=exp_node_key,
                prices=exp_prices,
            )
            
            # add arc without fixed losses from import node to export
            arc_tech_IE = Arcs(
                name="IE",
                # efficiency=[1, 1, 1, 1],
                efficiency={(0, 0): 1, (0, 1): 1, (0, 2): 1, (0, 3): 1},
                efficiency_reverse=None,
                static_loss=None,
                validate=False,
                capacity=[0.5, 1.0, 2.0],
                minimum_cost=[5, 5.1, 5.2],
                specific_capacity_cost=1,
                capacity_is_instantaneous=False,
            )
            mynet.add_directed_arc(
                node_key_a=imp_node_key, node_key_b=exp_node_key, arcs=arc_tech_IE
            )
        
            # identify node types
            mynet.identify_node_types()
        
            # no sos, regular time intervals
            ipp = self.build_solve_ipp(
                solver_options={},
                perform_analysis=False,
                plot_results=False,  # True,
                print_solver_output=False,
                networks={"mynet": mynet},
                time_frame=tf,
                static_losses_mode=InfrastructurePlanningProblem.STATIC_LOSS_MODE_DEP,
                mandatory_arcs=[],
                max_number_parallel_arcs={}
            )
        
            # *********************************************************************
            # *********************************************************************
            
            # import prices are higher: it makes no sense to install the arc
            # the arc should not be installed (unless prices allow for it)
    
            assert (
                True
                not in ipp.networks["mynet"]
                .edges[(imp_node_key, exp_node_key, 0)][Network.KEY_ARC_TECH]
                .options_selected
            )
    
            # overview
    
            (imports_qpk, 
             exports_qpk, 
             balance_qpk, 
             import_costs_qpk, 
             export_revenue_qpk, 
             ncf_qpk, 
             aggregate_static_demand_qpk,
             aggregate_static_supply_qpk,
             aggregate_static_balance_qpk) = statistics(ipp)
    
            abs_tol = 1e-3
            imports_qp = sum(imports_qpk[qpk] for qpk in tf.qpk() if qpk[1] == 0)
            assert math.isclose(imports_qp, 0.0, abs_tol=abs_tol)
    
            abs_tol = 1e-3
            import_costs_qp = sum(import_costs_qpk[qpk] for qpk in tf.qpk() if qpk[1] == 0)
            assert math.isclose(import_costs_qp, 0.0, abs_tol=abs_tol)
    
    
            # there should be no exports
            abs_tol = 1e-2
    
            exports_qp = sum(exports_qpk[(q, 0, k)] for k in tf.time_intervals[q])
            assert math.isclose(exports_qp, 0.0, abs_tol=abs_tol)
    
            export_revenue_qp = sum(export_revenue_qpk[(q, 0, k)] for k in tf.time_intervals[q])
            assert math.isclose(export_revenue_qp, 0.0, abs_tol=abs_tol)
    
    
            # there should be no capex
            abs_tol = 1e-6
            assert math.isclose(pyo.value(ipp.instance.var_capex), 0.0, abs_tol=abs_tol)
            
        # *************************************************************************
        # *************************************************************************
        
        def test_undirected_arc_static_upstream_new(self):
                    
            # assessment
            q = 0
            tf = EconomicTimeFrame(
                discount_rate=3.5/100,
                reporting_periods={q: (0, 1)},
                reporting_period_durations={q: (365 * 24 * 3600, 365 * 24 * 3600)},
                time_intervals={q: (0, 1)},
                time_interval_durations={q: (1, 1)},
            )
        
            # 4 nodes: two import nodes, two supply/demand nodes
        
            mynet = Network()
        
            # import nodes
            imp1_node_key = generate_pseudo_unique_key(mynet.nodes())
            mynet.add_import_node(
                node_key=imp1_node_key,
                prices={
                    qpk: ResourcePrice(prices=qpk[2] + 1, volumes=None)
                    for qpk in tf.qpk()
                },
            )
            imp2_node_key = generate_pseudo_unique_key(mynet.nodes())
            mynet.add_import_node(
                node_key=imp2_node_key,
                prices={
                    qpk: ResourcePrice(prices=2-qpk[2], volumes=None)
                    for qpk in tf.qpk()
                },
            )
        
            # other nodes
            node_A = generate_pseudo_unique_key(mynet.nodes())
            mynet.add_source_sink_node(
                node_key=node_A, base_flow={(0, 0): 0.0, (0, 1): 1.1}
            )
            node_B = generate_pseudo_unique_key(mynet.nodes())
            mynet.add_source_sink_node(
                node_key=node_B, base_flow={(0, 0): 1.1, (0, 1): 0.0}
            )
        
            # add arcs
            # I1A
            mynet.add_preexisting_directed_arc(
                node_key_a=imp1_node_key,
                node_key_b=node_A,
                efficiency=None,
                static_loss=None,
                capacity=1.2,
                capacity_is_instantaneous=False,
            )
        
            # I2B
            mynet.add_preexisting_directed_arc(
                node_key_a=imp2_node_key,
                node_key_b=node_B,
                efficiency=None,
                static_loss=None,
                capacity=1.2,
                capacity_is_instantaneous=False,
            )
            efficiency_AB = {(0, 0): 1, (0, 1): 1}
            efficiency_BA = {(0, 0): 1, (0, 1): 1}
            
            # AB
            static_loss_AB = {
                (0, q, 0): 0.1,
                (0, q, 1): 0.1,
                (1, q, 0): 0.1,
                (1, q, 1): 0.1,
            }
    
            arcs_ab = Arcs(
                name="AB",
                efficiency=efficiency_AB,
                efficiency_reverse=efficiency_BA,
                static_loss=static_loss_AB,
                capacity=(
                    0.5,
                    1,
                ),
                minimum_cost=(
                    0.025,
                    0.05,
                ),
                specific_capacity_cost=0,
                capacity_is_instantaneous=False,
                validate=True,
            )
    
            arc_key_AB_und = mynet.add_undirected_arc(
                node_key_a=node_A, node_key_b=node_B, arcs=arcs_ab
            )
    
        
            # identify node types
        
            mynet.identify_node_types()
        
            # no sos, regular time intervals
            
            for static_losses_mode in InfrastructurePlanningProblem.STATIC_LOSS_MODES:
                
                # reset decisions if necessary
                if True in mynet.edges[(imp1_node_key, node_A, 0)][Network.KEY_ARC_TECH].options_selected:
                    mynet.edges[(imp1_node_key, node_A, 0)][
                        Network.KEY_ARC_TECH].options_selected[
                            mynet.edges[(imp1_node_key, node_A, 0)][
                                Network.KEY_ARC_TECH].options_selected.index(True)
                            ] = False
                if True in mynet.edges[(imp2_node_key, node_B, 0)][Network.KEY_ARC_TECH].options_selected:
                    mynet.edges[(imp2_node_key, node_B, 0)][
                        Network.KEY_ARC_TECH].options_selected[
                            mynet.edges[(imp2_node_key, node_B, 0)][
                                Network.KEY_ARC_TECH].options_selected.index(True)
                            ] = False
                if True in mynet.edges[(node_A, node_B, arc_key_AB_und)][Network.KEY_ARC_TECH].options_selected:
                    mynet.edges[(node_A, node_B, arc_key_AB_und)][
                        Network.KEY_ARC_TECH].options_selected[
                            mynet.edges[(node_A, node_B, arc_key_AB_und)][
                                Network.KEY_ARC_TECH].options_selected.index(True)
                            ] = False
                
                ipp = self.build_solve_ipp(
                    solver_options={},
                    use_sos_arcs=False,
                    arc_sos_weight_key=None,
                    arc_use_real_variables_if_possible=False,
                    use_sos_sense=False,
                    sense_sos_weight_key=None,
                    sense_use_real_variables_if_possible=False,
                    sense_use_arc_interfaces=False,
                    perform_analysis=False,
                    plot_results=False,  # True,
                    print_solver_output=False,
                    networks={"mynet": mynet},
                    time_frame=tf,
                    static_losses_mode=static_losses_mode,
                    mandatory_arcs=[],
                    max_number_parallel_arcs={}
                )
            
                # all arcs should be installed (they are not new)
            
                assert (
                    True
                    in ipp.networks["mynet"]
                    .edges[(imp1_node_key, node_A, 0)][Network.KEY_ARC_TECH]
                    .options_selected
                )
            
                assert (
                    True
                    in ipp.networks["mynet"]
                    .edges[(imp2_node_key, node_B, 0)][Network.KEY_ARC_TECH]
                    .options_selected
                )
            
                assert (
                    True
                    in ipp.networks["mynet"]
                    .edges[(node_A, node_B, arc_key_AB_und)][Network.KEY_ARC_TECH]
                    .options_selected
                )
            
                # overview
    
                (imports_qpk, 
                 exports_qpk, 
                 balance_qpk, 
                 import_costs_qpk, 
                 export_revenue_qpk, 
                 ncf_qpk, 
                 aggregate_static_demand_qpk,
                 aggregate_static_supply_qpk,
                 aggregate_static_balance_qpk) = statistics(ipp)
    
            
                # the flow through AB should be from A to B during interval 0
            
                abs_tol = 1e-6
            
                assert math.isclose(
                    pyo.value(
                        ipp.instance.var_zeta_sns_glljqk[
                            ("mynet", node_A, node_B, arc_key_AB_und, q, 0)
                        ]
                    ),
                    1,
                    abs_tol=abs_tol,
                )
            
                assert math.isclose(
                    pyo.value(
                        ipp.instance.var_zeta_sns_glljqk[
                            ("mynet", node_B, node_A, arc_key_AB_und, q, 0)
                        ]
                    ),
                    0,
                    abs_tol=abs_tol,
                )
            
                # the flow through AB should be from B to A during interval 1
            
                assert math.isclose(
                    pyo.value(
                        ipp.instance.var_zeta_sns_glljqk[
                            ("mynet", node_A, node_B, arc_key_AB_und, q, 1)
                        ]
                    ),
                    0,
                    abs_tol=abs_tol,
                )
            
                assert math.isclose(
                    pyo.value(
                        ipp.instance.var_zeta_sns_glljqk[
                            ("mynet", node_B, node_A, arc_key_AB_und, q, 1)
                        ]
                    ),
                    1,
                    abs_tol=abs_tol,
                )
            
                # there should be imports
            
                abs_tol = 1e-6
    
                imports_qp = sum(imports_qpk[qpk] for qpk in tf.qpk() if qpk[1] == 0)
                assert math.isclose(imports_qp, (1.2 + 1.2), abs_tol=abs_tol)
    
            
                # there should be no exports
            
                abs_tol = 1e-6
    
                exports_qp = sum(exports_qpk[(q, 0, k)] for k in tf.time_intervals[q])
                assert math.isclose(exports_qp, 0, abs_tol=abs_tol)
    
    3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705 3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875 3876 3877 3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 3899 3900 3901 3902 3903 3904 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 3928
            
                # flow through I1A must be 1.0 during time interval 0
                # flow through I1A must be 0.2 during time interval 1
            
                abs_tol = 1e-6
            
                assert math.isclose(
                    pyo.value(ipp.instance.var_v_glljqk[("mynet", imp1_node_key, node_A, 0, 0, 0)]),
                    1.0,
                    abs_tol=abs_tol,
                )
            
                abs_tol = 1e-6
            
                assert math.isclose(
                    pyo.value(ipp.instance.var_v_glljqk[("mynet", imp1_node_key, node_A, 0, 0, 1)]),
                    0.2,
                    abs_tol=abs_tol,
                )
            
                # flow through I2B must be 0.2 during time interval 0
                # flow through I2B must be 1.0 during time interval 1
            
                abs_tol = 1e-6
            
                assert math.isclose(
                    pyo.value(ipp.instance.var_v_glljqk[("mynet", imp2_node_key, node_B, 0, 0, 0)]),
                    0.2,
                    abs_tol=abs_tol,
                )
            
                abs_tol = 1e-6
            
                assert math.isclose(
                    pyo.value(ipp.instance.var_v_glljqk[("mynet", imp2_node_key, node_B, 0, 0, 1)]),
                    1.0,
                    abs_tol=abs_tol,
                )
            
                # flow from B to A must be 0 during time interval 0
                # flow from A to B must be 0 during time interval 1
            
                assert math.isclose(
                    pyo.value(
                        ipp.instance.var_v_glljqk[("mynet", node_B, node_A, arc_key_AB_und, 0, 0)]
                    ),
                    0.0,
                    abs_tol=abs_tol,
                )
            
                assert math.isclose(
                    pyo.value(
                        ipp.instance.var_v_glljqk[("mynet", node_A, node_B, arc_key_AB_und, 0, 1)]
                    ),
                    0.0,
                    abs_tol=abs_tol,
                )
            
                # validation
            
                if static_losses_mode == InfrastructurePlanningProblem.STATIC_LOSS_MODE_ARR:
                    # arrival node
            
                    # flow from A to B must be 1.0 during time interval 0
            
                    assert math.isclose(
                        pyo.value(
                            ipp.instance.var_v_glljqk[
                                ("mynet", node_A, node_B, arc_key_AB_und, 0, 0)
                            ]
                        ),
                        1.0,
                        abs_tol=abs_tol,
                    )
            
                    # flow from B to A must be 0.9 during time interval 1
            
                    assert math.isclose(
                        pyo.value(
                            ipp.instance.var_v_glljqk[
                                ("mynet", node_B, node_A, arc_key_AB_und, 0, 1)
                            ]
                        ),
                        0.9,
                        abs_tol=abs_tol,
                    )
            
                elif static_losses_mode == InfrastructurePlanningProblem.STATIC_LOSS_MODE_DEP:
                    # departure node
            
                    # flow from A to B must be 0.9 during time interval 0
            
                    assert math.isclose(
                        pyo.value(
                            ipp.instance.var_v_glljqk[
                                ("mynet", node_A, node_B, arc_key_AB_und, 0, 0)
                            ]
                        ),
                        0.9,
                        abs_tol=abs_tol,
                    )
            
                    # flow from B to A must be 1.0 during time interval 1
            
                    assert math.isclose(
                        pyo.value(
                            ipp.instance.var_v_glljqk[
                                ("mynet", node_B, node_A, arc_key_AB_und, 0, 1)
                            ]
                        ),
                        1.0,
                        abs_tol=abs_tol,
                    )
            
                elif static_losses_mode == InfrastructurePlanningProblem.STATIC_LOSS_MODE_US:
                    # upstream
            
                    # flow from A to B must be 0.9 during time interval 0
            
                    assert math.isclose(
                        pyo.value(
                            ipp.instance.var_v_glljqk[
                                ("mynet", node_A, node_B, arc_key_AB_und, 0, 0)
                            ]
                        ),
                        0.9,
                        abs_tol=abs_tol,
                    )
            
                    # flow from B to A must be 0.9 during time interval 1
            
                    assert math.isclose(
                        pyo.value(
                            ipp.instance.var_v_glljqk[
                                ("mynet", node_B, node_A, arc_key_AB_und, 0, 1)
                            ]
                        ),
                        0.9,
                        abs_tol=abs_tol,
                    )
            
                else:
                    # downstream
            
                    # flow from A to B must be 1.0 during time interval 0
            
                    assert math.isclose(
                        pyo.value(
                            ipp.instance.var_v_glljqk[
                                ("mynet", node_A, node_B, arc_key_AB_und, 0, 0)
                            ]
                        ),
                        1.0,
                        abs_tol=abs_tol,
                    )
            
                    # flow from B to A must be 1.0 during time interval 1
            
                    assert math.isclose(
                        pyo.value(
                            ipp.instance.var_v_glljqk[
                                ("mynet", node_B, node_A, arc_key_AB_und, 0, 1)
                            ]
                        ),
                        1.0,
                        abs_tol=abs_tol,
                    )
                    
            # *********************************************************************
            # *********************************************************************
            
        # *************************************************************************
        # *************************************************************************
        
        def test_undirected_arc_static_upstream_preexisting(self):
            
            # assessment
            q = 0
            tf = EconomicTimeFrame(
                discount_rate=3.5/100,
                reporting_periods={q: (0, 1)},
                reporting_period_durations={q: (365 * 24 * 3600, 365 * 24 * 3600)},
                time_intervals={q: (0, 1)},
                time_interval_durations={q: (1, 1)},
            )
        
            # 4 nodes: two import nodes, two supply/demand nodes
        
            mynet = Network()
        
            # import nodes
            imp1_node_key = generate_pseudo_unique_key(mynet.nodes())
            mynet.add_import_node(
                node_key=imp1_node_key,
                prices={
                    qpk: ResourcePrice(prices=qpk[2] + 1, volumes=None)
                    for qpk in tf.qpk()
                },
            )
            imp2_node_key = generate_pseudo_unique_key(mynet.nodes())
            mynet.add_import_node(
                node_key=imp2_node_key,
                prices={
                    qpk: ResourcePrice(prices=2-qpk[2], volumes=None)
                    for qpk in tf.qpk()
                },
            )
        
            # other nodes
            node_A = generate_pseudo_unique_key(mynet.nodes())
            mynet.add_source_sink_node(
                node_key=node_A, base_flow={(0, 0): 0.0, (0, 1): 1.1}
            )
            node_B = generate_pseudo_unique_key(mynet.nodes())
            mynet.add_source_sink_node(
                node_key=node_B, base_flow={(0, 0): 1.1, (0, 1): 0.0}
            )
        
            # add arcs
            # I1A
            mynet.add_preexisting_directed_arc(
                node_key_a=imp1_node_key,
                node_key_b=node_A,
                efficiency=None,
                static_loss=None,
                capacity=1.2,
                capacity_is_instantaneous=False,
            )
        
            # I2B
            mynet.add_preexisting_directed_arc(
                node_key_a=imp2_node_key,
                node_key_b=node_B,
                efficiency=None,
                static_loss=None,
                capacity=1.2,
                capacity_is_instantaneous=False,
            )
            efficiency_AB = {(0, 0): 1, (0, 1): 1}
            efficiency_BA = {(0, 0): 1, (0, 1): 1}
            
            # AB
            static_loss_AB = {(0, q, 0): 0.1, (0, q, 1): 0.1}
            arc_key_AB_und = mynet.add_preexisting_undirected_arc(
                node_key_a=node_A,
                node_key_b=node_B,
                efficiency=efficiency_AB,
                efficiency_reverse=efficiency_BA,
                static_loss=static_loss_AB,
                capacity=1,
                capacity_is_instantaneous=False,
            )
        
            # identify node types
        
            mynet.identify_node_types()
        
            # no sos, regular time intervals
            
            for static_losses_mode in InfrastructurePlanningProblem.STATIC_LOSS_MODES:
                
                # reset decisions if necessary
                if True in mynet.edges[(imp1_node_key, node_A, 0)][Network.KEY_ARC_TECH].options_selected:
                    mynet.edges[(imp1_node_key, node_A, 0)][
                        Network.KEY_ARC_TECH].options_selected[
                            mynet.edges[(imp1_node_key, node_A, 0)][
                                Network.KEY_ARC_TECH].options_selected.index(True)
                            ] = False
                if True in mynet.edges[(imp2_node_key, node_B, 0)][Network.KEY_ARC_TECH].options_selected:
                    mynet.edges[(imp2_node_key, node_B, 0)][
                        Network.KEY_ARC_TECH].options_selected[
                            mynet.edges[(imp2_node_key, node_B, 0)][
                                Network.KEY_ARC_TECH].options_selected.index(True)
                            ] = False
                if True in mynet.edges[(node_A, node_B, arc_key_AB_und)][Network.KEY_ARC_TECH].options_selected:
                    mynet.edges[(node_A, node_B, arc_key_AB_und)][
                        Network.KEY_ARC_TECH].options_selected[
                            mynet.edges[(node_A, node_B, arc_key_AB_und)][
                                Network.KEY_ARC_TECH].options_selected.index(True)
                            ] = False
                
                ipp = self.build_solve_ipp(
                    solver_options={},
                    use_sos_arcs=False,
                    arc_sos_weight_key=None,
                    arc_use_real_variables_if_possible=False,
                    use_sos_sense=False,
                    sense_sos_weight_key=None,
                    sense_use_real_variables_if_possible=False,
                    sense_use_arc_interfaces=False,
                    perform_analysis=False,
                    plot_results=False,  # True,
                    print_solver_output=False,
                    networks={"mynet": mynet},
                    time_frame=tf,
                    static_losses_mode=static_losses_mode,
                    mandatory_arcs=[],
                    max_number_parallel_arcs={}
                )
            
                # all arcs should be installed (they are not new)
            
                assert (
                    True
                    in ipp.networks["mynet"]
                    .edges[(imp1_node_key, node_A, 0)][Network.KEY_ARC_TECH]
                    .options_selected
                )
            
                assert (
                    True
                    in ipp.networks["mynet"]
                    .edges[(imp2_node_key, node_B, 0)][Network.KEY_ARC_TECH]
                    .options_selected
                )
            
                assert (
                    True
                    in ipp.networks["mynet"]
                    .edges[(node_A, node_B, arc_key_AB_und)][Network.KEY_ARC_TECH]
                    .options_selected
                )
            
                # overview
    
                (imports_qpk, 
                 exports_qpk, 
                 balance_qpk, 
                 import_costs_qpk, 
                 export_revenue_qpk, 
                 ncf_qpk, 
                 aggregate_static_demand_qpk,
                 aggregate_static_supply_qpk,
                 aggregate_static_balance_qpk) = statistics(ipp)
    
            
                # the flow through AB should be from A to B during interval 0
            
                abs_tol = 1e-6
            
                assert math.isclose(
                    pyo.value(
                        ipp.instance.var_zeta_sns_glljqk[
                            ("mynet", node_A, node_B, arc_key_AB_und, q, 0)
                        ]
                    ),
                    1,
                    abs_tol=abs_tol,
                )
            
                assert math.isclose(
                    pyo.value(
                        ipp.instance.var_zeta_sns_glljqk[
                            ("mynet", node_B, node_A, arc_key_AB_und, q, 0)
                        ]
                    ),
                    0,
                    abs_tol=abs_tol,
                )
            
                # the flow through AB should be from B to A during interval 1
            
                assert math.isclose(
                    pyo.value(
                        ipp.instance.var_zeta_sns_glljqk[
                            ("mynet", node_A, node_B, arc_key_AB_und, q, 1)
                        ]
                    ),
                    0,
                    abs_tol=abs_tol,
                )
            
                assert math.isclose(
                    pyo.value(
                        ipp.instance.var_zeta_sns_glljqk[
                            ("mynet", node_B, node_A, arc_key_AB_und, q, 1)
                        ]
                    ),
                    1,
                    abs_tol=abs_tol,
                )
            
                # there should be imports
            
                abs_tol = 1e-6
    
                imports_qp = sum(imports_qpk[qpk] for qpk in tf.qpk() if qpk[1] == 0)
                assert math.isclose(imports_qp, (1.2 + 1.2), abs_tol=abs_tol)
    
    
                exports_qp = sum(exports_qpk[(q, 0, k)] for k in tf.time_intervals[q])
                export_revenue_qp = sum(export_revenue_qpk[(q, 0, k)] for k in tf.time_intervals[q])
                assert math.isclose(exports_qp, 0, abs_tol=abs_tol)
    
            
                # flow through I1A must be 1.0 during time interval 0