Calculating relay operating times, according to IEC 60255
You need not declare # -*- coding: utf-8 -*- in Python 3: UTF-8 is the default.

Writing I = np.logspace(…) inside your curve_equation function is bad practice, because it hard-codes certain x-values for your plot.  In fact, your plot is wrong, because the calculations are for \$I_s\$ values ranging from 102 A to 105 A (due to the inner I = np.logspace(np.log10(Is), 5, 1e2)), but your plot's x axis is labelled as if the values ranged from 101 A to 105 A (due to the outer I = np.logspace(1, 5, 1e2)).  Specifically, four of the curves should intersect at 1 kA, 10 s.

I also find the way in which the curves are specified very cumbersome.  You have five magic strings ("SIT", "VIT", etc.) which you search for in curve_types.  Then, you retrieve the k, alpha, and beta parameters by using the corresponding index.  Printing an error message and returning None is an inappropriate error-handling mechanism; you should raise or propagate an exception for such a fatal error.

I'd rather specify the parameters the other way: each curve should be defined as a \$(k, \alpha, \beta)\$ tuple.  To do that, I would use namedtuples.  I'd go a bit further and make each namedtuple smart enough to know what its own \$td(I)\$ function looks like, when \$T\$ and \$I_s\$ are specified.

from collections import namedtuple
import numpy as np
import matplotlib.pyplot as plt

class IEC_Curve(namedtuple('IEC_Curve', 'name k alpha beta')):
    def td(self, T, Is):
        """
        The td(I) function for a given IEC curve, time dial setting, and
        current setting.
        """
        return lambda I: self.k * T / self.beta / ((I / Is)**self.alpha - 1)

SIT = IEC_Curve('SIT', k=0.14, alpha=0.02, beta=2.97)   # Standard inverse / A
VIT = IEC_Curve('VIT', k=13.5, alpha=1, beta=1.5)       # Very inverse / B
LTI = IEC_Curve('LTI', k=120, alpha=2, beta=13.33)      # Long time inverse / B
EIT = IEC_Curve('EIT', k=80, alpha=2, beta=0.808)       # Extremely inverse / C
UIT = IEC_Curve('UIT', k=315.2, alpha=2.5, beta=1)      # Ultra inverse

I = np.logspace(2, 5, 1e2)
for curve in (SIT, VIT, LTI, EIT, UIT):
    plt.loglog(I, curve.td(T=1, Is=1e2)(I), label=curve.name)
plt.xlabel("Current [A]")
plt.ylabel("Time [s]")
plt.title("IEC Time/current curves")
plt.grid()
plt.legend()
plt.show()




Note that td is a method that returns a function.  To calculate values of \$td\$ with fixed \$T\$, fixed \$I_s\$, and swept \$I\$:

UIT.td(T=1, Is=1e2)(I=np.logspace(2, 5, 1e2))


To calculate a value of \$td\$ for a given \$T\$, \$I_s\$, and \$I\$:

UIT.td(T=1, Is=1e2)(I=1e2)


(This happens to cause a ZeroDivisionError, which manifests itself as a RuntimeWarning: divide by zero encountered in divide at the beginning of the sweep.)



In case you feel intimidated by my use of inheritance of namedtuple, you could use a manually written constructor instead:

class IEC_Curve:
    def __init__(self, name, k, alpha, beta):
        self.name = name
        self.k = k
        self.alpha = alpha
        self.beta = beta

    def td(self, T, Is):
        return lambda I: self.k * T / self.beta / ((I / Is)**self.alpha - 1)


One difference is that namedtuples are immutable, whereas the members here are mutable.