[Python] Barycentric method Loss Calculation from delunary tringles [closed]

Discussão em 'Python' iniciado por Stack, Março 15, 2025.

  1. Stack

    Stack Membro Participativo

    Triangular File Explanation:


    The triangular file consists of multiple triangles generated through Delaunay triangulation, partitioning the state-of-charge (SOC) and power space into smaller segments. Each triangle is defined by three vertices, each containing:

    • State-of-charge (SOC, in %)
    • Power (in kW)
    • Corresponding losses (in kW)

    These triangles allow efficient and accurate piecewise-linear interpolation for battery losses, essential for optimization accuracy.

    Barycentric Coordinates Function Explanation:


    Barycentric coordinates (a, b, c) are used to interpolate within each triangle. They represent weights for each vertex, calculated as follows:

    def barycentric_coordinates(self, x1, y1, x2, y2, x3, y3, x, y):
    v0x, v0y = x2 - x1, y2 - y1
    v1x, v1y = x3 - x1, y3 - y1
    denom = v0x * v1y - v1x * v0y
    inv_denom = 1.0 / denom

    a = (v1y * (x - x1) + v0x * (y - y1)) * inv_denom
    b = (v0y * (x - x1) + v1x * (y - y1)) * inv_denom
    c = 1.0 - a - b

    return a, b, c

    Model Implementation for Charging and Discharging:


    The implementation ensures accurate loss calculations using barycentric coordinates within selected triangles.

    M = 100 # Big-M method constant for constraint relaxation

    for period in range(self.SimPeriods):
    soc_rfb = (e_rfb[period] / self.RFBCapacity) * 100 # SOC percentage

    # === Charging Mode ===
    charging_loss_expr = gp.LinExpr()

    # Iterate through charging triangles to calculate losses
    for i, tri in enumerate(self.charge_triangles):
    v1, v2, v3 = tri["vertices"]
    x1, y1, z1 = v1
    x2, y2, z2 = v2
    x3, y3, z3 = v3

    # Calculate barycentric coordinates
    a, b, c = self.barycentric_coordinates(x1, y1, x2, y2, x3, y3, soc_rfb, p_to_rfb[period])

    # Binary variables for checking validity of coordinates
    valid_a_ch = self.Problem.addVar(vtype=gp.GRB.BINARY, name=f"valid_a_ch_{period}_{i}")
    valid_b_ch = self.Problem.addVar(vtype=gp.GRB.BINARY, name=f"valid_b_ch_{period}_{i}")
    valid_c_ch = self.Problem.addVar(vtype=gp.GRB.BINARY, name=f"valid_c_ch_{period}_{i}")

    # Constraints to ensure coordinates are within valid range [0,1]
    self.Problem.addConstr(a >= 0 - M * (1 - valid_a_ch))
    self.Problem.addConstr(a <= 1 + M * valid_a_ch)
    self.Problem.addConstr(b >= 0 - M * (1 - valid_b_ch))
    self.Problem.addConstr(b <= 1 + M * valid_b_ch)
    self.Problem.addConstr(c >= 0 - M * (1 - valid_c_ch))
    self.Problem.addConstr(c <= 1 + M * valid_c_ch)

    # Activate delta_ch if triangle conditions are valid
    self.Problem.addConstr(delta_ch[period, i] <= valid_a_ch)
    self.Problem.addConstr(delta_ch[period, i] <= valid_b_ch)
    self.Problem.addConstr(delta_ch[period, i] <= valid_c_ch)
    self.Problem.addConstr(delta_ch[period, i] >= valid_a_ch + valid_b_ch + valid_c_ch - 2)

    # Compute loss expression if triangle is selected
    charging_loss_expr = delta_ch[period, i] * (a * z1 + b * z2 + c * z3)

    # Constraint ensuring exactly one triangle is selected per period for charging
    self.Problem.addConstr(p_aux_losscharging_rfb_ch[period] == charging_loss_expr)

    # === Discharging Mode ===
    discharging_loss_expr = gp.LinExpr()

    for i, tri in enumerate(self.discharge_triangles):
    v1, v2, v3 = tri["vertices"]
    x1, y1, z1 = v1
    x2, y2, z2 = v2
    x3, y3, z3 = v3

    # Calculate barycentric coordinates
    a, b, c = self.barycentric_coordinates(x1, y1, x2, y2, x3, y3, soc_rfb, p_from_rfb[period])

    valid_a_dch = self.Problem.addVar(vtype=gp.GRB.BINARY, name=f"valid_a_dch_{period}_{i}")
    valid_b_dch = self.Problem.addVar(vtype=gp.GRB.BINARY, name=f"valid_b_dch_{period}_{i}")
    valid_c_dch = self.Problem.addVar(vtype=gp.GRB.BINARY, name=f"valid_c_dch_{period}_{i}")

    self.Problem.addConstr(a >= 0 - M * (1 - valid_a_dch))
    self.Problem.addConstr(a <= 1 + M * valid_a_dch)
    self.Problem.addConstr(b >= 0 - M * (1 - valid_b_dch))
    self.Problem.addConstr(b <= 1 + M * valid_b_dch)
    self.Problem.addConstr(c >= 0 - M * (1 - valid_c_dch))
    self.Problem.addConstr(c <= 1 + M * valid_c_dch)

    self.Problem.addConstr(delta_dch[period, i] <= valid_a_dch)
    self.Problem.addConstr(delta_dch[period, i] <= valid_b_dch)
    self.Problem.addConstr(delta_dch[period, i] <= valid_c_dch)
    self.Problem.addConstr(delta_dch[period, i] >= valid_a_dch + valid_b_dch + valid_c_dch - 2)

    discharging_loss_expr = delta_dch[period, i] * (a * z1 + b * z2 + c * z3)

    self.Problem.addConstr(p_aux_lossdischarging_rfb_dch[period] == discharging_loss_expr)

    Ensuring Single Triangle Selection:


    To guarantee the selection of exactly one triangle, add:

    self.Problem.addConstr(gp.quicksum(delta_ch[period, i] for i in range(num_triangles)) == 1)
    self.Problem.addConstr(gp.quicksum(delta_dch[period, i] for i in range(num_triangles)) == 1)


    This ensures the solver selects only the optimal triangle, effectively ignoring others.

    i got calcualtion of loses as zero and whetaher my logic is right or not and the constrin i have put using bycentirc method

    Continue reading...

Compartilhe esta Página