# Crossing conditions in ModelingToolkit @mtkmodel macro

**URL:** <https://discourse.julialang.org/t/crossing-conditions-in-modelingtoolkit-mtkmodel-macro/123780>\
**Category:** Modelling & Simulations\
**Tags:** modelingtoolkit, differentialequation\
**Created:** [December 12, 2024, 6:31pm UTC](https://discourse.julialang.org/t/crossing-conditions-in-modelingtoolkit-mtkmodel-macro/123780 "2024-12-12T18:31:11Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![nurban](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nurban/32/5278_2.png) [@nurban](https://discourse.julialang.org/u/nurban)\
**Post date:** [December 12, 2024, 6:31pm UTC](https://discourse.julialang.org/t/crossing-conditions-in-modelingtoolkit-mtkmodel-macro/123780/1 "2024-12-12T18:31:12Z")

</div>

I want to add a continuous event trigger to my @mtkmodel, with a downcrossing condition. Without the crossing direction condition, I think this should look like

```julia
@mtkmodel odesys begin
    @variables begin
        X(t)
        Y(t)
    end
...
    @continuous_events begin
        [X ~ X_thresh] => [Y ~ Y/2]
    end
end

```

Is there a way to specify downcrossings within the macro, or do I have to abandon @mtkmodel and construct everything explicitly/procedurally by building an ODESystem passing in arrays of equations, variables, events, etc.?

---

<div class="post-metadata">

**Author:** ![nurban](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nurban/32/5278_2.png) [@nurban](https://discourse.julialang.org/u/nurban)\
**Post date:** [December 16, 2024, 3:30pm UTC](https://discourse.julialang.org/t/crossing-conditions-in-modelingtoolkit-mtkmodel-macro/123780/2 "2024-12-16T15:30:12Z")

</div>

I haven’t found any way of doing this automatically within the macro system; I think the macros automatically guarantee that all continuous events trigger on both up- and down-crossings.

The next-best solution I found was to pull info out of the @mtkmodel system with the incorrect positive+negative affects, manually construct a downcrossing event with only a negative affect, and then manually rebuild the ODE system with it. I also had to hack around with some equation substitutions to handle the external forcing code my model has; the `substitute` code below may not be necessary for all models.

Code:

```julia
@mtkbuild odesys_ref = odesys()

# create a continuous event callback that triggers on the old model's threshold crossing but only takes action on downcrossings
ce = continuous_events(odesys_ref)[1] # old model's continuous event definition
cb_down = ModelingToolkit.SymbolicContinuousCallback(
    eqs = ce.eqs,
    affect = Equation[], # positive-edge affect = nothing (was ce.affect in old model); turn off upcrossing trigger
    affect_neg = ce.affect, # negative-edge affect (ce.affect); downcrossing trigger is what the old model used
    rootfind = SciMLBase.LeftRootFind,
    reinitializealg = SciMLBase.CheckInit()
)

# regenerate a new ODESystem from the old @mtkmodel-generated ODESystem, but with the new downcrossing event callback added
substitutions = Dict(eq.lhs => eq.rhs for eq in observed(odesys_ref))
substituted_eqs = [substitute(eq, substitutions) for eq in equations(odesys_ref)] # make sure the external forcing time series is represented in the right place
@named odesys_new = ODESystem(substituted_eqs, get_iv(odesys_ref), unknowns(odesys_ref), parameters(odesys_ref), observed=observed(odesys_ref), continuous_events=[cb_down])

odesys = structural_simplify(odesys_new);

```
