Skip to content
LIBRARY
Math.Tests.Product.md

Math.Tests.Product

Multiplies constant and time-varying values together.

This component takes two constant values (3 and 2) and multiplies them together using a Product block, resulting in an output value of 6. The component demonstrates the basic connection patterns for creating a multiplication operation between constant signals in the modeling language. A second Product block is driven by two sine waves that sweep negative and positive values so the product varies over time.

Usage

BlockComponents.Math.Tests.Product()

Behavior

julia
using BlockComponents #hide
using ModelingToolkit #hide
@named sys = BlockComponents.Math.Tests.Product() #hide
let eqs = full_equations(sys); Base.length(eqs) > 25 ? nothing : eqs end #hide
<< @example-block not executed in draft mode >>

Source

dyad
"""
Multiplies constant and time-varying values together.

This component takes two constant values (3 and 2) and multiplies them together using a Product block,
resulting in an output value of 6. The component demonstrates the basic connection patterns for creating
a multiplication operation between constant signals in the modeling language. A second Product block is
driven by two sine waves that sweep negative and positive values so the product varies over time.
"""
test component Product
  "Constant block that outputs the value 3"
  c1 = BlockComponents.Sources.Constant(k = 3) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 320, "y1": 60, "x2": 420, "y2": 160, "rot": 0}
      },
      "tags": []
    }
  }
  "Constant block that outputs the value 2"
  c2 = BlockComponents.Sources.Constant(k = 2) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 320, "y1": 270, "x2": 420, "y2": 370, "rot": 0}
      },
      "tags": []
    }
  }
  "Product block that multiplies its two inputs"
  product = BlockComponents.Math.Product() {
    "Dyad": {
      "placement": {
        "icon": {"iconName": "product", "x1": 950, "y1": 450, "x2": 1050, "y2": 550, "rot": 0},
        "diagram": {"iconName": "product", "x1": 580, "y1": 170, "x2": 680, "y2": 270, "rot": 0}
      },
      "tags": []
    }
  }
  "Sine source for the first input of the second block"
  sine1 = BlockComponents.Sources.Sine(amplitude = 3, frequency = 1) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 330, "y1": 570, "x2": 430, "y2": 670, "rot": 0}
      },
      "tags": []
    }
  }
  "Sine source for the second input of the second block"
  sine2 = BlockComponents.Sources.Sine(amplitude = 2, frequency = 1) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 330, "y1": 780, "x2": 430, "y2": 880, "rot": 0}
      },
      "tags": []
    }
  }
  "Second Product block driven by the sine sources"
  product_2 = BlockComponents.Math.Product() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "product", "x1": 590, "y1": 680, "x2": 690, "y2": 780, "rot": 0}
      },
      "tags": []
    }
  }
relations
  connect(c1.y, product.u1) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 500, "y": 110}, {"x": 500, "y": 190}], "E": 2}],
      "renderStyle": "standard"
    }
  }
  connect(product.u2, c2.y) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 500, "y": 250}, {"x": 500, "y": 320}], "E": 2}],
      "renderStyle": "standard"
    }
  }
  connect(sine1.y, product_2.u1) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 500, "y": 620}, {"x": 500, "y": 700}], "E": 2}],
      "renderStyle": "standard"
    }
  }
  connect(product_2.u2, sine2.y) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 500, "y": 760}, {"x": 500, "y": 830}], "E": 2}],
      "renderStyle": "standard"
    }
  }
metadata {
  "Dyad": {
    "icons": {"default": "dyad://BlockComponents/Example.svg"},
    "tests": {
      "case1": {
        "stop": 5,
        "expect": {"signals": ["product.y", "product_2.y", "sine1.y", "sine2.y"]}
      }
    }
  }
}
end
Flattened Source
dyad
"""
Multiplies constant and time-varying values together.

This component takes two constant values (3 and 2) and multiplies them together using a Product block,
resulting in an output value of 6. The component demonstrates the basic connection patterns for creating
a multiplication operation between constant signals in the modeling language. A second Product block is
driven by two sine waves that sweep negative and positive values so the product varies over time.
"""
test component Product
  "Constant block that outputs the value 3"
  c1 = BlockComponents.Sources.Constant(k = 3) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 320, "y1": 60, "x2": 420, "y2": 160, "rot": 0}
      },
      "tags": []
    }
  }
  "Constant block that outputs the value 2"
  c2 = BlockComponents.Sources.Constant(k = 2) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 320, "y1": 270, "x2": 420, "y2": 370, "rot": 0}
      },
      "tags": []
    }
  }
  "Product block that multiplies its two inputs"
  product = BlockComponents.Math.Product() {
    "Dyad": {
      "placement": {
        "icon": {"iconName": "product", "x1": 950, "y1": 450, "x2": 1050, "y2": 550, "rot": 0},
        "diagram": {"iconName": "product", "x1": 580, "y1": 170, "x2": 680, "y2": 270, "rot": 0}
      },
      "tags": []
    }
  }
  "Sine source for the first input of the second block"
  sine1 = BlockComponents.Sources.Sine(amplitude = 3, frequency = 1) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 330, "y1": 570, "x2": 430, "y2": 670, "rot": 0}
      },
      "tags": []
    }
  }
  "Sine source for the second input of the second block"
  sine2 = BlockComponents.Sources.Sine(amplitude = 2, frequency = 1) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 330, "y1": 780, "x2": 430, "y2": 880, "rot": 0}
      },
      "tags": []
    }
  }
  "Second Product block driven by the sine sources"
  product_2 = BlockComponents.Math.Product() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "product", "x1": 590, "y1": 680, "x2": 690, "y2": 780, "rot": 0}
      },
      "tags": []
    }
  }
relations
  connect(c1.y, product.u1) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 500, "y": 110}, {"x": 500, "y": 190}], "E": 2}],
      "renderStyle": "standard"
    }
  }
  connect(product.u2, c2.y) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 500, "y": 250}, {"x": 500, "y": 320}], "E": 2}],
      "renderStyle": "standard"
    }
  }
  connect(sine1.y, product_2.u1) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 500, "y": 620}, {"x": 500, "y": 700}], "E": 2}],
      "renderStyle": "standard"
    }
  }
  connect(product_2.u2, sine2.y) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 500, "y": 760}, {"x": 500, "y": 830}], "E": 2}],
      "renderStyle": "standard"
    }
  }
metadata {
  "Dyad": {
    "icons": {"default": "dyad://BlockComponents/Example.svg"},
    "tests": {
      "case1": {
        "stop": 5,
        "expect": {"signals": ["product.y", "product_2.y", "sine1.y", "sine2.y"]}
      }
    }
  }
}
end


Test Cases

julia
using BlockComponents
using DyadInterface: TransientAnalysis, rebuild_sol, ODEAlg
using ModelingToolkit: toggle_namespacing, get_initial_conditions, @named
using CSV, DataFrames, Plots

snapshotsdir = joinpath(dirname(dirname(pathof(BlockComponents))), "test", "snapshots")
<< @setup-block not executed in draft mode >>

Test Case case1

julia
@named model_case1 = BlockComponents.Math.Tests.Product()
model_case1 = toggle_namespacing(model_case1, false)

model_case1 = toggle_namespacing(model_case1, true)
result_case1 = TransientAnalysis(; model = model_case1, alg = ODEAlg.Auto(), start = 0e+0, stop = 5e+0, abstol=1e-6, reltol=1e-6)
sol_case1 = rebuild_sol(result_case1)
<< @setup-block not executed in draft mode >>
julia
df_case1 = DataFrame(:t => sol_case1[:t], :actual => sol_case1[model_case1.product.y])
dfr_case1 = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Math.Tests.Product_case1_sig0.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_case1, idxs=[model_case1.product.y], width=2, label="Actual value of product.y")
if !isnothing(dfr_case1)
  scatter!(plt, dfr_case1.t, dfr_case1.expected, mc=:red, ms=3, label="Expected value of product.y")
end
<< @setup-block not executed in draft mode >>
julia
plt
<< @example-block not executed in draft mode >>
julia
df_case1 = DataFrame(:t => sol_case1[:t], :actual => sol_case1[model_case1.product_2.y])
dfr_case1 = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Math.Tests.Product_case1_sig1.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_case1, idxs=[model_case1.product_2.y], width=2, label="Actual value of product_2.y")
if !isnothing(dfr_case1)
  scatter!(plt, dfr_case1.t, dfr_case1.expected, mc=:red, ms=3, label="Expected value of product_2.y")
end
<< @setup-block not executed in draft mode >>
julia
plt
<< @example-block not executed in draft mode >>
julia
df_case1 = DataFrame(:t => sol_case1[:t], :actual => sol_case1[model_case1.sine1.y])
dfr_case1 = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Math.Tests.Product_case1_sig2.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_case1, idxs=[model_case1.sine1.y], width=2, label="Actual value of sine1.y")
if !isnothing(dfr_case1)
  scatter!(plt, dfr_case1.t, dfr_case1.expected, mc=:red, ms=3, label="Expected value of sine1.y")
end
<< @setup-block not executed in draft mode >>
julia
plt
<< @example-block not executed in draft mode >>
julia
df_case1 = DataFrame(:t => sol_case1[:t], :actual => sol_case1[model_case1.sine2.y])
dfr_case1 = try CSV.read(joinpath(snapshotsdir, "BlockComponents.Math.Tests.Product_case1_sig3.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_case1, idxs=[model_case1.sine2.y], width=2, label="Actual value of sine2.y")
if !isnothing(dfr_case1)
  scatter!(plt, dfr_case1.t, dfr_case1.expected, mc=:red, ms=3, label="Expected value of sine2.y")
end
<< @setup-block not executed in draft mode >>
julia
plt
<< @example-block not executed in draft mode >>