# ABM - Scheduler.bytype just for 1 agent function

**URL:** https://discourse.julialang.org/t/abm-scheduler-bytype-just-for-1-agent-function/107611
**Category:** Modelling & Simulations
**Tags:** agents, agentsjl
**Created:** [December 14, 2023, 1:45pm UTC](https://discourse.julialang.org/t/abm-scheduler-bytype-just-for-1-agent-function/107611 "2023-12-14T13:45:48Z")
**Posts on this page:** 3
**Page:** 1

<div class="post-metadata">

### Author: ![elisadonati23](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/elisadonati23/32/205472_2.png) [@elisadonati23](https://discourse.julialang.org/u/elisadonati23)
#### Post date: [December 14, 2023, 1:45pm UTC](https://discourse.julialang.org/t/abm-scheduler-bytype-just-for-1-agent-function/107611/1 "2023-12-14T13:45:48Z")

</div>

Dear all,  
I am running an ABM model with NoSpaceAgent representing sardines populations over time with three lifestages identified by the field type::Symbol and with 2 sexes (sex::Symbol).  
Adults can spawn in a period of the year and I am trying to apply a weird reproduction rule (based on a previous model which was written in NetLogo); the function was letting reproducing bigger females first with the biggest male available. Males should never reproduce more than 10 times (10 different females; Engaged::Int64 \< 10).  
I created a MWE down here, a simplification of my simulation with comments and already my question listed, that I will report also here.

1. basically all the functions working on the agents actually do not need the `Sardine` (agent) argument as I wrote them. In my simulation instead, other functions need the arguments `function(agent,model)` to work. This is confusing to me and I wonder if it can cause problems. especially` adultspawn` use a loop to go through the females and males, create copies of them and the access the `model.agents[id]` to modify the features. I guess it’s not the proper way to work but I started translating a previous ABM from Netlogo which was working in this way.

2.` adultspawn` can be modified work through an advanced` scheduler.bytype`? From the documentation for advanced scheduling I didn’t get how, it seems to me that the scheduler would be valid for all the functions in the simulation, yet I want to use the `scheduler.bytype` just for `adultspawn`

1. I don’t understand why the `reset_engaged(Sardine, model)` version in the comment is not working.

Could you give me some advice or explanations? It would be amazing. Thank you all in advance!

```julia
using Agents

@agent Sardine NoSpaceAgent begin
    type::Symbol #adult
    sex::Symbol #male,female
    engaged::Int64
    size::Int64 #number of times the agent reproduced in 1 timestep
end

function generate_adult(N, model)
    for _ in 1:N
        type_agent = :adult
        sex_agent = rand((:male, :female))
        engaged_agent = 0
        size_agent = rand(100:200)
        add_agent!(Sardine, model, type_agent, sex_agent, engaged_agent, size_agent) 
    end
    return
end

# support function to sort agents by size
function sort_agent(agent_type, sex, model, feature)
    all_agents = collect(values(allagents(model)))
    sorted_agents = sort(filter(agent -> (agent.type == agent_type) && (agent.sex == sex), all_agents), by=agent -> getfield(agent,Symbol(feature)), rev=true)
    return sorted_agents
end

#scheduler default faster
properties = Dict(:timesteps => Int64(0))
model = ABM(Sardine; properties=properties)
generate_adult(40, model)

# stepping functions --
#increase timestep
 function evolve_model(model)
    model.timesteps += 1
    println("timestep ", model.timesteps)
    return
 end

 # each timestep I reset engaged and let them spawn
 function sardine_step(model) 
    # (?.1)here Sardine argument is useless, in my complete simulation 
    # however I have other function tht need Sardine argument to work at each timestep.
    reset_engaged(model)
    adultspawn(model)
    return
end

function reset_engaged(model) 
    for agent in allagents(model)
        agent.engaged = 0
    end
    return
end

# (?.2) not clear why this alternative was not working
#function reset_engaged(Sardine, model) 
# Sardine.engaged = 0
# return
#end

function adultspawn(model)
    #sort males and females by size
    sorted_female = sort_agent(:adult, :female, model, "size")
    sorted_males = sort_agent(:adult, :male, model, "size")

    for f in sorted_female

        current_male_index = 1
        #check if there are no males
        if isempty(sorted_males)
            println("no males available")
            break
        else
            partner = sorted_males[current_male_index]
        end

        #choose the biggest male for each sorted female
        if partner.engaged >= 10 #number of times reproduced
            #if reproduced more than 10 times, pick the second biggest male
            current_male_index += 1
            if current_male_index > length(sorted_males)
                println("No more males available for reproduction")
                break
            else
                partner = sorted_males[current_male_index]
            end
        end

        #add event of reproduction
        partner.engaged += 1
        f.engaged += 1

        # Update the agents in the model
        model.agents[partner.id].engaged = partner.engaged
        model.agents[f.id].engaged = f.engaged
    end

    return
end

# run the model and store results

adata = [:sex, :size, :engaged]
df_agent = init_agent_dataframe(model, adata)

df_agent = run!(model, sardine_step, evolve_model,100; adata)
#takes approx 1 sec at timestep --> slooow

df_agent1 = sort!(df_agent[1], :size, rev=true)
show(df_agent1, allrows=true, allcols=true)

#seems to work but it seems slow and I would like to use a scheduler.bytype 
#for adultspawn to not create copies of the agents but call the Sardine argument. 
#Is it possible?

```

---

<div class="post-metadata">

### Author: ![fbanning](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/fbanning/32/14972_2.png) [@fbanning](https://discourse.julialang.org/u/fbanning)
#### Post date: [December 14, 2023, 2:30pm UTC](https://discourse.julialang.org/t/abm-scheduler-bytype-just-for-1-agent-function/107611/2 "2023-12-14T14:30:58Z")

</div>

In Agents.jl the idea of `agent_step!` and `model_step!` functions are used. The former describes what a single agent should do each time step when they’re activated. The latter describes everything else that should happen in the model each time step. That’s why their function signatures are `agent_step!(agent, model)` and `model_step!(model)` respectively. The order by which agents are activated (i.e. do the thing inside your `agent_step!` function) is determined by the scheduler function. Of course you can call your own agent\_step! and model\_step! functions whatever you want (like you already did in your code) but you should stick to the arguments they take in.

> [@elisadonati23](#):
>
> ```julia
> # (?.2) not clear why this alternative was not working
> #function reset_engaged(Sardine, model) 
> # Sardine.engaged = 0
> # return
> #end
> 
> ```

You’ve put `Sardine` here instead of `sardine`. Note the capital `S` which references your agent type that you’ve defined at the beginning of the file. You want to use `sardine` (or any other name for that argument) to reference an instance of `Sardine` which is one of your agents. If you want to define that the `reset_engaged` function can _only_ be called with `Sardine` agents, you can specify the type `reset_engaged(sardine::Sardine, model)`.

> [@elisadonati23](#):
>
> ```julia
> # each timestep I reset engaged and let them spawn
> function sardine_step(model) 
> # (?.1)here Sardine argument is useless, in my complete simulation 
> # however I have other function tht need Sardine argument to work at each timestep.
> reset_engaged(model)
> adultspawn(model)
> return
> end
> 
> ```

1. Make it `sardine_step(sardine, model)`
2. Consider moving `reset_engaged(model)` (or just its content, i.e. the for-loop) into your `evolve_model(model)` function.
3. Make it `adultspawn(sardine, model)`
4. Separate the sorting logic for `sorted_female` and `sorted_males` from the actual agent stepping function. The `sorted_female` list should be your scheduler function (i.e. which agents get activated in which order) and the `sorted_males` list could for example be stored as a model property that gets updated once per timestep inside the `evolve_model` function.
5. Now that you’re scheduling according to your sorted list of adult females, you can remove the for-loop from inside the `adultspawn` function. This is now handled by Agents.jl in the background, so you don’t have to take care about looping over the set of agents anymore.
6. To first have your model reset the engaged status of your sardines, add the `agents_first` keyword argument to your `run!` call: `run!(model, sardine_step, evolve_model,100; adata, agents_first = false)` This now first executes your `evolve_model` function once and then your `sardine_step` function for each agent. See [here in the documentation](https://juliadynamics.github.io/Agents.jl/stable/tutorial/#CommonSolve.step!).

I probably forgot a few things but hopefully this information gets you started and helps you get your model running correctly. Feel free to share your progress on this and also feel free to ask if you have further questions.

---

<div class="post-metadata">

### Author: ![elisadonati23](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/elisadonati23/32/205472_2.png) [@elisadonati23](https://discourse.julialang.org/u/elisadonati23)
#### Post date: [January 4, 2024, 5:25pm UTC](https://discourse.julialang.org/t/abm-scheduler-bytype-just-for-1-agent-function/107611/3 "2024-01-04T17:25:25Z")

</div>

Many Many Thanks! I had time now to read in deep your comment and suggestion and now it’s much more clear to me.  
I will try what you suggested and in case back here. Thank you again!
