derived

pmrf.models.adapters.derived.derived(fn: Callable[[...], Model]) → Callable[[...], Derived]

Turns a function of a base model and new parameters into a derived model constructor.

A derived model starts from a nominal model and derives a more complete one that captures some artifact (a wet section, degraded copper, a cut). The decorated function is called as f(base, **new): exactly one positional base (a model, or any collection of models and parameters) and keyword arguments that are the new parameters (a pmrf.Param, or anything pmrf.as_param() accepts). It returns a pmrf.Model that holds the base and the new parameters once, and calls fn on them whenever the model is used.

fn receives the base and the new parameters as the model is unwrapped, so parameters arrive as physical values, as for pmrf.tie(). It must return a pmrf.Model, must be pure, and its output’s structure and port count must not depend on parameter values. Inside fn, use pmrf.replace() for fields of the object in hand and pmrf.update() for parts reached by name.

Names. The base’s parameters keep the names they have on the base, and each new parameter is named by its keyword. Nothing produced inside fn is named. The derived model takes the base’s name unless name= is passed, so a container prefixes both as usual. A derived model can be the base of another, and names accumulate flat; a parameter shared by several parts is expressed by deriving at the level that owns it.

fn is a static part of the model: two models derived with the same function share a jit cache entry. Define it once at module level; a lambda created on every call recompiles.

Parameters:

fn (Callable) – fn(base, **new) -> Model.

Returns:

constructor(base, *, name=None, **new) -> Derived.

Return type:

Callable

Raises:
  • TypeError – On a call with other than one positional base or with no new parameters, or (when the model is used) if fn does not return a model.

  • ValueError – If a keyword clashes with a parameter name of the base.

Examples

A coaxial cable of total length L that is wet for its first wet_length:

@prf.derived
def wet(cable, wet_length, wet_ep_r):
    wet = prf.replace(cable, length=wet_length,
                      dielectric=prf.replace(cable.dielectric, ep_r=wet_ep_r))
    dry = prf.replace(cable, length=cable.length - wet_length)
    return wet ** dry

coax = wet(coax, wet_length=prf.Random(Uniform(0, 20), scale=1e-3),
           wet_ep_r=prf.Random(Uniform(1, 80)))
prf.params(coax)   # the cable's names, plus 'wet_length' and 'wet_ep_r'