def find_steady_state(tfwd, ckt, t12min=500e-9, fswmax=100e3): # print(f'finding steady-state for tfwd={fmt(tfwd)}s...') def eq(v0): # print(f' trying from v0={fmt(v0, 4)}V') dv, ss = sim(v0, ckt, (tfwd, t12min)) fsw = 1 / sum(s.dt for s in ss) if fsw > fswmax: def eqf(t12): _, ss = sim(v0, ckt, (tfwd, t12)) fsw = 1 / sum(s.dt for s in ss) return fsw - fswmax t12 = nsolve(eqf, t12min, 1 / fswmax) dv, ss = sim(v0, ckt, (tfwd, t12)) return dv, ss eq(75.16256356353576) # FIXME: delete v0max = ckt.vbus v0min = -ckt.vout / ckt.lm * (ckt.lr + ckt.lm) v0 = nsolve(lambda v: eq(v)[0], v0min * .99 + v0max * .01, v0max * .99 + v0min * .01) residue, ss = eq(v0) if abs(residue) > MINIMUM_VOLTAGE: print(f'Warning: cannot find steady-state for tfwd = {fmt(tfwd)}s.') return ss
def test_case_01(): ckt = ahbllc.AHBLLCTrafo(lr=25e-6, lm=1225e-6, cr=39e-9, nps=4.3, vbus=410, vload=20, chb=500e-12) con = 200e-9 v0 = nsolve(lambda v: ahbllc.sim(v, ckt, (con, 500e-9))[0], -ckt.vbus, ckt.vout / ckt.lm * (ckt.lr + ckt.lm)) _, ss = ahbllc.sim(v0, ckt, (con, 500e-9)) fig, *_ = plot(ss, show=True) ev = ahbllc.evaluate_switching_period(ss) print(ev.set_circuit(ckt)) plt.close(fig)
def sim(v0, ckt, con): commutating = lambda s: s in {state_c0, state_c1} active = lambda s: s not in {state_c0, state_c1} def max_dv(i0, v0): l = ckt.lr + ckt.lm c = 1 / (1 / ckt.cr + 1 / ckt.chb) z = (l / c)**.5 r = math.hypot(v0, i0 * z) phi = math.atan2(v0, i0 * z) return r * (1 + math.sin(phi)) def eq_zvon(t): _, ls_on = _sim_phase(ls_on_inf, active, hs_off[-1].i1, hs_off[-1].v1, hs_off[-1].vhb1, hs_off[-1].im1, ckt, t) return max_dv(ls_on[-1].i1, ls_on[-1].v1) - (1 + ckt.chb / ckt.cr) * ckt.vbus conv, t12min = con i0 = 0 vhb0 = ckt.vbus # high-side on phase nsf, hs_on = _sim_phase(state_h0, active, i0, v0, vhb0, i0, ckt, conv) # high-side off phase ls_on_inf, hs_off = _sim_phase(nsf, commutating, hs_on[-1].i1, hs_on[-1].v1, hs_on[-1].vhb1, hs_on[-1].im1, ckt, None) # low-side on phase _, ls_on = _sim_phase(ls_on_inf, active, hs_off[-1].i1, hs_off[-1].v1, hs_off[-1].vhb1, hs_off[-1].im1, ckt, t12min) if max_dv(ls_on[-1].i1, ls_on[-1].v1) < (1 + ckt.chb / ckt.cr) * ckt.vbus: t12max = (math.pi - ls_on[-1].phi) / ls_on[-1].w if t12min < t12max <= MAXIMUM_T12: if 0 < eq_zvon(t12max): t12_zvon = nsolve(eq_zvon, t12min, t12max) else: t12_zvon = t12max _, ls_on = _sim_phase(ls_on_inf, active, hs_off[-1].i1, hs_off[-1].v1, hs_off[-1].vhb1, hs_off[-1].im1, ckt, t12_zvon) # low-side off phase _, ls_off = _sim_phase(state_c0, commutating, ls_on[-1].i1, ls_on[-1].v1, ls_on[-1].vhb1, ls_on[-1].im1, ckt, None) # high-side on phase _, hs_on2 = _sim_phase(state_h0, active, ls_off[-1].i1, ls_off[-1].v1, ls_off[-1].vhb1, ls_off[-1].im1, ckt, None) return hs_on2[-1].v1 - v0, hs_on + hs_off + ls_on + ls_off + hs_on2
def evaluate_operating_point(pout, ckt, t12min=500e-9, fswmax=100e3): pmax = evaluate_switching_period( find_steady_state(ckt.vbus, ckt, t12min, fswmax)).iout * ckt.vout if 0 < pout <= pmax: voff = nsolve( lambda v: evaluate_switching_period( find_steady_state(v, ckt, t12min, fswmax)).iout * ckt.vout - pout, ckt.vout / 10, ckt.vbus) ss = find_steady_state(voff, ckt, t12min, fswmax) return voff, ss, evaluate_switching_period(ss) else: return 0, [State()], Evaluation()
def eq(v0): # print(f' trying from v0={fmt(v0, 4)}V') dv, ss = sim(v0, ckt, (tfwd, t12min)) fsw = 1 / sum(s.dt for s in ss) if fsw > fswmax: def eqf(t12): _, ss = sim(v0, ckt, (tfwd, t12)) fsw = 1 / sum(s.dt for s in ss) return fsw - fswmax t12 = nsolve(eqf, t12min, 1 / fswmax) dv, ss = sim(v0, ckt, (tfwd, t12)) return dv, ss
def state_h0_vincscomp(i0, v0, vhb0, im0, ckt, con, state_func_list): """Sub-state of: h - high-side device on, 0 - output rectifier off. Exit criteria: high-side turned off when v reaches voff vincscomp - voltage increment control with slope compensation """ del vhb0, im0 dvoff = con chb = math.inf cr = ckt.cr ctot = 1 / (1 / cr + 1 / chb) ltot = ckt.lr + ckt.lm w = (ltot * ctot)**-.5 z = (ltot / ctot)**.5 vhb0 = ckt.vbus vout = 0 vcen = vhb0 + vout r = math.hypot(v0 - vcen, i0 * z) phi = math.atan2(v0 - vcen, i0 * z) # i = r * cos(w * t + phi) / z # v = r * sin(w * t + phi) + vcen # vhb = vbus # im = i slope = 40 / 10e-6 if i0 == 0: # v0 + dvoff - slope * t == r * sin(w * t + phi) + vcen dt = nsolve( lambda t: v0 + dvoff - slope * t - r * math.sin(w * t + phi) - vcen, 0, math.pi / w) # dt = min((math.asin((v0 + dvoff - vcen) / r) - phi) % (2 * math.pi), # (math.pi - math.asin((v0 + dvoff - vcen) / r) - phi) % (2 * math.pi)) / w else: # 0 = r * cos(w * t + phi) / z, w * t + phi = -pi / 2 dt = ((-math.pi / 2 - phi) % (2 * math.pi)) / w i1 = r * math.cos(w * dt + phi) / z v1 = r * math.sin(w * dt + phi) + vcen next_state = state_func_list['state_c0'] return next_state, State(state='h0', dt=dt, i0=i0, v0=v0, vhb0=vhb0, im0=i0, i1=i1, v1=v1, vhb1=vhb0, im1=i1, r=r, phi=phi, w=w, z=z, cr=cr, chb=chb, vcen=vcen, vout=vout, km=0) # yapf: disable
def eq(i): di, _, ss = sim(i, ckt, (voff, t12min)) fsw = 1 / sum(s.dt for s in ss) if fsw > fswmax: def eqf(t): _, _, ss = sim(i, ckt, (voff, t)) fsw = 1 / sum(s.dt for s in ss) return fsw - fswmax t12 = nsolve(eqf, .5e-6, 1 / fswmax) di, _, ss = sim(i, ckt, (voff, t12)) dv = ss[-1].v1 - voff return di, dv, ss
def state_l1(i0, v0, vhb0, im0, ckt, con): """Sub-state of: l - low-side device on, 1 - output rectifier on. Exit criteria: resonant current == magnetizing current (solve the Kepler's Equation) """ del vhb0, con # vhb == 0 chb = math.inf cr = ckt.cr ctot = 1 / (1 / cr + 1 / chb) ltot = ckt.lr w = (ltot * ctot)**-.5 z = (ltot / ctot)**.5 km = ckt.vout / ckt.lm vhb0 = 0 vout = ckt.vout vcen = vhb0 + vout r = math.hypot(v0 - vcen, i0 * z) phi = math.atan2(v0 - vcen, i0 * z) # i = r * cos(w * t + phi) / z # v = r * sin(w * t + phi) + vcen # vhb = 0 # im = im0 - km * t ta = tb = (-phi % (2 * math.pi)) / w while r * math.cos(w * ta + phi) / z > im0 - km * ta and ta > MINIMUM_TIME: ta /= 2 if ta > MINIMUM_TIME: dt = nsolve(lambda t: im0 - km * t - r * math.cos(w * t + phi) / z, ta, tb) else: dt = ta i1 = r * math.cos(w * dt + phi) / z v1 = r * math.sin(w * dt + phi) + vout next_state = state_l0 return next_state, State(state='l1', dt=dt, i0=i0, v0=v0, vhb0=vhb0, im0=im0, i1=i1, v1=v1, vhb1=vhb0, im1=i1, r=r, phi=phi, w=w, z=z, cr=cr, chb=chb, vcen=vcen, vout=vout, km=-km) # yapf: disable
def evaluate_operating_point(pout, ckt, t12min=500e-9, fswmax=100e3): # tfwdmax = math.pi / 2 / ((ckt.lr + ckt.lm) * ckt.cr)**-.5 vdion = ckt.vout / ckt.lm * (ckt.lr + ckt.lm) tfwdmax = (math.pi / 2 + math.asin(vdion / (ckt.vbus + vdion))) / ( (ckt.lr + ckt.lm) * ckt.cr)**-.5 sspmax = find_steady_state(tfwdmax, ckt, t12min, fswmax) pmax = evaluate_switching_period(sspmax).iout * ckt.vout if 0 < pout <= pmax: evaluate_switching_period( find_steady_state(MINIMUM_FORWARD_TIME, ckt, t12min, fswmax)) # FIXME: debug only tf = nsolve( lambda t: evaluate_switching_period( find_steady_state(t, ckt, t12min, fswmax)).iout * ckt.vout - pout, MINIMUM_FORWARD_TIME, tfwdmax) ss = find_steady_state(tf, ckt, t12min, fswmax) return tf, ss, evaluate_switching_period(ss), pmax else: return 0, [State()], Evaluation(), pmax
def find_steady_state(voff, ckt, t12min=500e-9, fswmax=100e3): def eq(i): di, _, ss = sim(i, ckt, (voff, t12min)) fsw = 1 / sum(s.dt for s in ss) if fsw > fswmax: def eqf(t): _, _, ss = sim(i, ckt, (voff, t)) fsw = 1 / sum(s.dt for s in ss) return fsw - fswmax t12 = nsolve(eqf, .5e-6, 1 / fswmax) di, _, ss = sim(i, ckt, (voff, t12)) dv = ss[-1].v1 - voff return di, dv, ss i0max = ckt.vbus / ((ckt.lr + ckt.lm) / ckt.cr)**.5 i0 = nsolve(lambda i: eq(i)[0], .001, i0max * 2) _, _, ss = eq(i0) return ss
def state_c1(i0, v0, vhb0, im0, ckt, con): """Sub-state of: c - both devices are off, half-bridge behaving like a capacitor, 1 - output rectifier on. This state occurs when 1) we consider the commutation, and 2) in heavy load where the output diode turns on immediately after the turning-off of the high-side MOS. Exit criteria: (1) half-bridge voltage reaches DC bus or 0 --> h1 (prevented in control strategy) or l1 (2) Exit criteria: resonant current == magnetizing current (solve the Kepler's Equation) --> c0 very unlikely to happen (but it's *not* impossible, consider the condition where Lm is extremely small) """ del con chb = ckt.chb cr = ckt.cr ctot = 1 / (1 / cr + 1 / chb) ltot = ckt.lr w = (ltot * ctot)**-.5 z = (ltot / ctot)**.5 vcen = vout = ckt.vout vcap0 = v0 - vhb0 r = math.hypot(vcap0 - vcen, i0 * z) phi = math.atan2(vcap0 - vcen, i0 * z) km = ckt.vout / ckt.lm # i(t) == r * cos(w * t + phi) / z, resonant current # i(0) == i0 <= 0, guaranteed by previous state # vcap(t) = r * sin(w * t + phi) + vcen, voltage across two capacitors # v(t) == r * sin(w * t + phi) / (1 + cr / chb) + # v0 / (1 + chb / cr) + # (vhb0 + vout) / (1 + cr / chb) # vhb(t) == -r * sin(w * t + phi) / (1 + chb / cr) - # (vout - v0) / (1 + chb / cr) + # vhb0 / (1 + cr / chb) v = 0 # target of the resonant voltage v v -= -(vout - v0) / (1 + chb / cr) + vhb0 / (1 + cr / chb) v *= (1 + chb / cr) if -r <= v <= r: dtcomm = min((math.asin(v / -r) - phi) % (2 * math.pi), (math.pi - math.asin(v / -r) - phi) % (2 * math.pi)) / w else: # hard-switching, which is allowed dtcomm = (math.pi / 2 - phi) % (2 * math.pi) / w ta = tb = (-phi % (2 * math.pi)) / w while r * math.cos(w * ta + phi) / z > im0 - km * ta and ta > MINIMUM_TIME: ta /= 2 if ta > MINIMUM_TIME: dtdiof = nsolve(lambda t: im0 - km * t - r * math.cos(w * t + phi) / z, ta, tb) else: dtdiof = ta if dtdiof < dtcomm: # diode turns off before commutation finishes (vhb reduces to 0), almost impossible dt = dtdiof next_state = state_c0 else: dt = dtcomm next_state = state_l1 i1 = r * math.cos(w * dt + phi) / z v1 = (r * math.sin(w * dt + phi) / (1 + cr / chb) + v0 / (1 + chb / cr) + (vhb0 + vout) / (1 + cr / chb)) vhb1 = (-r * math.sin(w * dt + phi) / (1 + chb / cr) - (vout - v0) / (1 + chb / cr) + vhb0 / (1 + cr / chb)) if -1e-6 < vhb1 - ckt.vbus < 1e-6: vhb1 = ckt.vbus elif -1e-6 < vhb1 < 1e-6: vhb1 = 0 im1 = im0 - km * dt if -1e-6 < im1 - i1 < 1e-6: im1 = i1 return next_state, State(state='c1', dt=dt, i0=i0, v0=v0, vhb0=vhb0, im0=im0, i1=i1, v1=v1, vhb1=vhb1, im1=im1, r=r, phi=phi, w=w, z=z, cr=cr, chb=chb, vcen=vcen, vout=vout, km=-km) # yapf: disable
def sim_voff(i0, ckt, con): sfl = dict(state_l1=state_l1, state_l0=state_l0, state_h0=state_h0_voff, state_c0=state_c0, state_c1=state_c1) commutating = lambda s: s in {sfl['state_c0'], sfl['state_c1']} active = lambda s: s not in {sfl['state_c0'], sfl['state_c1']} def eq_zvon(t, hs_off_fins): # from the end moment of the high-side turning-off state (high- to low-side commutation finishes), # find out what t12 gives ZV-on of the high-side device. _, ss = _sim_phase(isf_ls_on, active, *hs_off_fins, ckt, t, sfl) ls_on_fins = (ss[-1].i1, ss[-1].v1, ss[-1].vhb1, ss[-1].im1) _, ss = _sim_phase(state_c0, commutating, *ls_on_fins, ckt, t, sfl) c0 = ss[-1] r, chb, cr = c0.r, c0.chb, c0.cr vout, v0, vhb0 = c0['vout'], c0.v0, c0.vhb0 vhbmax = r / (1 + chb / cr) - (vout - v0) / (1 + chb / cr) + vhb0 / ( 1 + cr / chb) return vhbmax - ckt.vbus # def eq_vcont(t, hs_off_fins, ratio_v_ls_on=.95): # # resonant voltage continuity equation: # # t should be so long that when we turn off the low-side, # # v the resonant voltage is at (or below) ratio_v_ls_on * voff # _, ss = _sim1(isf_ls_on, active, *hs_off_fins, ckt, t) # v_ls_on_fin = ss[-1].v1 # return v_ls_on_fin - voff * ratio_v_ls_on # def eq_vcont(t, hs_off_fins, ratio_v_ls_on=.95): # # resonant voltage continuity equation: # # t should be so long that when we turn off the low-side and go into the high-side on state, # # the minimum value of v, the resonant voltage, is at (or below) ratio_v_ls_on * voff # # 这样做看起来很理想,但是 # # 1) 无法实现,除非控制器有办法得知 hs_on 状态中 v 的最小值 # # 2) 会改变 sim 返回值 i_fin - i0 随 i0 的单调性,使方程无法可靠求解 # _, ss = _sim1(isf_ls_on, active, *hs_off_fins, ckt, t) # ls_on_fins = (ss[-1].i1, ss[-1].v1, ss[-1].vhb1, ss[-1].im1) # _, ss = _sim1(state_c0, commutating, *ls_on_fins, ckt, t) # v0 = ss[-1].v1 # i0 = ss[-1].i1 # z = ((ckt.lr + ckt.lm) / ckt.cr)**.5 # vcen = ckt.vbus # r = math.hypot(v0 - vcen, i0 * z) # vmin = -r + vcen # return vmin - voff * ratio_v_ls_on voff, t12min = con # starting at the turning-off moment of the high-side device v0 = voff vhb0 = ckt.vbus # high-side turning-off phase nsf, hs_off = _sim_phase(state_c0, commutating, i0, v0, vhb0, i0, ckt, None, sfl) hs_off_fins = (hs_off[-1].i1, hs_off[-1].v1, hs_off[-1].vhb1, hs_off[-1].im1) isf_ls_on = nsf # remember the initial state of the low-side on phase, # we may needed repetitively in determine t12 for high-side ZV-on # low-side on phase nsf, ls_on = _sim_phase(isf_ls_on, active, *hs_off_fins, ckt, t12min, sfl) ls_on_fins = (ls_on[-1].i1, ls_on[-1].v1, ls_on[-1].vhb1, ls_on[-1].im1) t12capm = ls_on[ -1].dt # state_l0 automatically increases t12 and avoids capactive mode # low-side turning-off phase nsf, ls_off = _sim_phase(state_c0, commutating, *ls_on_fins, ckt, None, sfl) ls_off_fins = (ls_off[-1].i1, ls_off[-1].v1, ls_off[-1].vhb1, ls_off[-1].im1) # correct t12 t12zvon = 0 t12max = (math.pi - ls_on[-1].phi) / ls_on[ -1].w # the t12 that generates the most negative current before low-side turns off if ls_off[ -1].vhb1 < ckt.vbus - MINIMUM_VOLTAGE: # hard-switching occurs, see if we can avoid it if eq_zvon(t12max, hs_off_fins) >= 0: # yes, we can t12zvon = nsolve(lambda t: eq_zvon(t, hs_off_fins), t12min / 2, t12max) else: # hard-switching we cannot avoid, in conditions, e.g., too much chb t12zvon = t12max t12 = max(t12min, t12capm, t12zvon) # t12vcont = 0 # in practice, the switching frequency is limited # if ls_off[-1].v1 > voff: # t12vcont = nsolve(lambda t: eq_vcont(t, hs_off_fins), t12min / 2, t12max) # t12 = max(t12, t12vcont) # t12 correction needed if t12 > t12min: # re-run the low-side on and low- to high-side commutation nsf, ls_on = _sim_phase(isf_ls_on, active, *hs_off_fins, ckt, t12, sfl) ls_on_fins = (ls_on[-1].i1, ls_on[-1].v1, ls_on[-1].vhb1, ls_on[-1].im1) nsf, ls_off = _sim_phase(state_c0, commutating, *ls_on_fins, ckt, None, sfl) ls_off_fins = (ls_off[-1].i1, ls_off[-1].v1, ls_off[-1].vhb1, ls_off[-1].im1) # high-side on phase _, hs_on = _sim_phase(state_h0_voff, active, *ls_off_fins, ckt, voff, sfl) res = hs_on[-1].i1 - i0 if hs_on[-1].v1 > voff + MINIMUM_VOLTAGE: dt_ls_off = sum(s.dt for s in ls_off) dt_hs_on = MAXIMUM_COMMUTATION_TIME - dt_ls_off if dt_hs_on < MINIMUM_HIGH_SIDE_ON_TIME: dt_hs_on = MINIMUM_HIGH_SIDE_ON_TIME hs_on_v0 = ls_off[-1].v1 hs_on_i0 = ls_off[-1].i1 hs_on_z = ((ckt.lr + ckt.lm) / ckt.cr)**.5 hs_on_w = ((ckt.lr + ckt.lm) * ckt.cr)**-.5 hs_on_vcen = ckt.vbus hs_on_r = math.hypot(hs_on_v0 - hs_on_vcen, hs_on_i0 * hs_on_z) hs_on_phi = math.atan2(hs_on_v0 - hs_on_vcen, hs_on_i0 * hs_on_z) hs_on_v1 = hs_on_r * math.sin(hs_on_w * dt_hs_on + hs_on_phi) + hs_on_vcen _, hs_on = _sim_phase(state_h0_voff, active, *ls_off_fins, ckt, hs_on_v1) # voltage continuity violates if we reach here, which indicates that # the i_fin - i0 function could have multiple zeros, and the solver may fail. # consider changing the res to keep it monotone res = hs_on[-1].i1 - i0 states = hs_off + ls_on + ls_off + hs_on return res, t12, states # TODO: 统一所有 simulator 的返回格式
def sim_dvoff(i0, v0, ckt, con): """Simulator used only for delta-V turning-off control. The first return variable is a tuple of two floats representing the residue current and voltage. :param i0: initial current :type i0: float :param v0: initial voltage :type v0: float :param ckt: circuit :type ckt: AHBLLC :param con: controlling variable :type con: tuple of two floats :return: residues and states :rtype: Tuple[residues, states] where residues::Tuple[float, float], states::State """ sfl = dict(state_l1=state_l1, state_l0=state_l0, state_h0=state_h0_dvoff, state_c0=state_c0, state_c1=state_c1) commutating = lambda s: s in {sfl['state_c0'], sfl['state_c1']} active = lambda s: s not in {sfl['state_c0'], sfl['state_c1']} def max_dv(i0, v0): l = ckt.lr + ckt.lm c = 1 / (1 / ckt.cr + 1 / ckt.chb) z = (l / c)**.5 r = math.hypot(v0, i0 * z) phi = math.atan2(v0, i0 * z) return r * (1 + math.sin(phi)) def eq_zvon(t): _, ls_on = _sim_phase(ls_on_inf, active, hs_off[-1].i1, hs_off[-1].v1, hs_off[-1].vhb1, hs_off[-1].im1, ckt, t, sfl) return max_dv(ls_on[-1].i1, ls_on[-1].v1) - (1 + ckt.chb / ckt.cr) * ckt.vbus dvoff, t12min = con vhb0 = ckt.vbus # high-side on phase nsf, hs_on = _sim_phase(state_h0_dvoff, active, i0, v0, vhb0, i0, ckt, dvoff, sfl) # high-side off phase ls_on_inf, hs_off = _sim_phase(nsf, commutating, hs_on[-1].i1, hs_on[-1].v1, hs_on[-1].vhb1, hs_on[-1].im1, ckt, None, sfl) # low-side on phase _, ls_on = _sim_phase(ls_on_inf, active, hs_off[-1].i1, hs_off[-1].v1, hs_off[-1].vhb1, hs_off[-1].im1, ckt, t12min, sfl) if max_dv(ls_on[-1].i1, ls_on[-1].v1) < (1 + ckt.chb / ckt.cr) * ckt.vbus: t12max = (math.pi - ls_on[-1].phi) / ls_on[-1].w if t12min < t12max: if 0 < eq_zvon(t12max): t12_zvon = nsolve(eq_zvon, t12min, t12max) else: t12_zvon = t12max _, ls_on = _sim_phase(ls_on_inf, active, hs_off[-1].i1, hs_off[-1].v1, hs_off[-1].vhb1, hs_off[-1].im1, ckt, t12_zvon, sfl) # low-side off phase _, ls_off = _sim_phase(state_c0, commutating, ls_on[-1].i1, ls_on[-1].v1, ls_on[-1].vhb1, ls_on[-1].im1, ckt, None, sfl) return (ls_off[-1].i1 - i0, ls_off[-1].v1 - v0), hs_on + hs_off + ls_on + ls_off
def sim(v0, ckt, con, constr=None): sfl = dict(state_l1=state_l1, state_l0=state_l0, state_h0=state_h0_tfwd, state_c0=state_c0, state_c1=state_c1) if constr is not None: sfl['state_h0'] = constr assert v0 < ckt.vbus commutating = lambda s: s in {sfl['state_c0'], sfl['state_c1']} active = lambda s: s not in {sfl['state_c0'], sfl['state_c1']} def max_dv(i0, v0): l = ckt.lr + ckt.lm c = 1 / (1 / ckt.cr + 1 / ckt.chb) z = (l / c)**.5 r = math.hypot(v0, i0 * z) phi = math.atan2(v0, i0 * z) return r * (1 + math.sin(phi)) def eq_zvon(t): _, ls_on = _sim_phase(ls_on_inf, active, hs_off[-1].i1, hs_off[-1].v1, hs_off[-1].vhb1, hs_off[-1].im1, ckt, t, sfl) return max_dv(ls_on[-1].i1, ls_on[-1].v1) - (1 + ckt.chb / ckt.cr) * ckt.vbus conv, t12min = con i0 = 0 vhb0 = ckt.vbus # high-side on phase nsf, hs_on = _sim_phase(sfl['state_h0'], active, i0, v0, vhb0, i0, ckt, conv, sfl) # high-side off phase ls_on_inf, hs_off = _sim_phase(nsf, commutating, hs_on[-1].i1, hs_on[-1].v1, hs_on[-1].vhb1, hs_on[-1].im1, ckt, None, sfl) # low-side on phase _, ls_on = _sim_phase(ls_on_inf, active, hs_off[-1].i1, hs_off[-1].v1, hs_off[-1].vhb1, hs_off[-1].im1, ckt, t12min, sfl) if max_dv(ls_on[-1].i1, ls_on[-1].v1) < (1 + ckt.chb / ckt.cr) * ckt.vbus: t12max = (math.pi - ls_on[-1].phi) / ls_on[-1].w if t12min < t12max: if 0 < eq_zvon(t12max): t12_zvon = nsolve(eq_zvon, t12min, t12max) else: t12_zvon = t12max _, ls_on = _sim_phase(ls_on_inf, active, hs_off[-1].i1, hs_off[-1].v1, hs_off[-1].vhb1, hs_off[-1].im1, ckt, t12_zvon, sfl) # low-side off phase _, ls_off = _sim_phase(sfl['state_c0'], commutating, ls_on[-1].i1, ls_on[-1].v1, ls_on[-1].vhb1, ls_on[-1].im1, ckt, None, sfl) # high-side on phase _, hs_on2 = _sim_phase(sfl['state_h0'], active, ls_off[-1].i1, ls_off[-1].v1, ls_off[-1].vhb1, ls_off[-1].im1, ckt, None, sfl) return hs_on2[-1].v1 - v0, hs_on + hs_off + ls_on + ls_off + hs_on2