0
$\begingroup$

I am trying to model a tank with discontinuous flow into the tank and discontinuous flow out of the tank. I do it numerically. You can imagine a tank to which I feed blue balls at timepoint t0, red balls at timepoint t1 and so on. I assume instantaneous mixing of the solutions in the tank. When I take out part of the solution in the tank, I will remove same proportions of the different colored balls. After some time, I will have removed more blue balls (i.e. balls that have been longer in the tank) than balls of a color that I added to a later point. I want to calculate the residence time distribution of let's say the blue balls over time.

I post here the first iteration steps to give you an idea. My problem is that I don't manage to program the iteration. I tried to use an approach with Do and Reap/Sow to get the results of each individual iteration, but I didn't manage to solve it.

I have two example lists needed for the problem (of same length): My real lists are much longer.

timeVolIn={{0,5},{1,5},{2,2},{3,1},{4,2}}(*this as the form of a nested list with {time,volIn}*)
volOut={1,5,3,2}(*volOut at each time*)

the first iteration:

input1=timeVolIn[[1;;2]]
currentVolTank=input1[[All,2]]//Total
volOutFactor=volOut[[2]]/currentVolTank
result1=If[volOutFactor==0,#*{1,1},#*{1,volOutFactor}]/@input1 (*the "If" is necessary because my volOut[[i]] can be zero. In this case I don't want anything to happen, therefore the *{1,1}*)

the second iteration:

input2=List[result1,timeVolIn[[3]]}
currentVolTank=input2[[All,2]]//Total
volOutFactor=volOut[[3]]/currentVolTank
result2=If[volOutFactor==0,#*{1,1},#*{1,volOutFactor}]/@input2

the third iteration:

input3=List[result2,timeVolIn[[4]]}
currentVolTank=input3[[All,2]]//Total
volOutFactor=volOut[[4]]/currentVolTank
result3=If[volOutFactor==0,#*{1,1},#*{1,volOutFactor}]/@input3

I think you get the kind of iteration I need from that. I want to collect all results. This would call for NestList, but I can't manage it. The output has to be a nested list in which each the first sublist (=result of the first iteration) is of length 2, the second sublist (=result of the second itereation is of length 3) and so on.

Thanks in advance!!

$\endgroup$
4
  • 2
    $\begingroup$ You would probably want to start by re-writing your iteration steps into a single function that you can apply to some starting value. You might also be interested in Fold / FoldList for this purpose if you have to pass a second argument to your iteration function, in addition to the result of the previous iteration. $\endgroup$
    – MarcoB
    Commented Nov 10, 2016 at 17:55
  • $\begingroup$ My problem with FoldList (or NestList) is that I don't know how to set up the first iteration. The first iteration has to be timeVolIn[[1;;2]], but in the second iteration onwards it is the OUTPUT of the last iteration. $\endgroup$
    – Niki
    Commented Nov 11, 2016 at 6:32
  • $\begingroup$ Niki, perhaps you might want to take another look at the NestList documentation. NestList in fact does exactly what you describe. For instance: NestList[function, startingValue, numberOfIterations] will return a list containing the starting value first, and then the results of repeatedly applying function to it. Perhaps you might want to look at the results of NestList[2#&, 3, 4]: this applies a doubling function four times, starting on the value 3, to give {3, 6, 12, 24, 48}. Is this not along the lines of what you need? $\endgroup$
    – MarcoB
    Commented Nov 11, 2016 at 15:15
  • $\begingroup$ I tried the following, but no success: i = 2; NestList[j = i++; If[volOut[[j]] == 0, #*{1, 1}, #*{1, (volOut[[j]]/(#[[All, 2]] // Total))}] & /@ input1 &, timeVolIn[[1 ;; 2]], (timeVolIn // Length - 1)] ... My problem is that the "input1" should be the result from the last iteration. So how can is use the "Map" inside the "NestList. I have just too many #, so I get lost. $\endgroup$
    – Niki
    Commented Nov 11, 2016 at 15:30

1 Answer 1

1
$\begingroup$

My problem is that the "input1" should be the result from the last iteration.

In such cases, FoldPair can be useful. We want something in this form:

FoldPair[
  <a function that performs each update>, 
  <initial state>, 
  <sequence of update arguments>]

For each update, we need to know how many new (non-blue) balls are added and how many balls are then removed (divided proportionally between blue/non-blue groups). So, we'll model our update argument as a list of two elements: {in, out}. Based on the info provided, the sequence of updates would be: {{5, 1}, {2, 5}, {1, 3}, {2, 2}} (although OP never seems to use the first value of volOut, so I may be confused here).

The state of our system can just be {blueCount, otherCount}.

Now we need a function that takes the current state and an update and returns a new state:

DiscreteUpdate[state : {blueCount_, otherCount_}, {in_, out_}] :=
  With[
    {tempState = {blueCount, otherCount + in}},
    tempState (1 - out/Total[tempState])]

I hope I've understood the behavior correctly.

Now, in FoldPair, the updating function actually needs to return a pair. The first item in the pair corresponds to the desired output, which might be more than just the state. The second item is the new state. Since we only care about the state--we won't apply any additional transformation on it--our FoldPair function will look redundant.

FlowSteps = {{5, 1}, {2, 5}, {1, 3}, {2, 2}};
FoldPair[{DiscreteUpdate[##], DiscreteUpdate[##]} &, {5, 0}, FlowSteps]

{72/77, 236/77}

If you want to see the whole sequence of intermediate results, use FoldPairList:

FoldPairList[{DiscreteUpdate[##], DiscreteUpdate[##]} &, {5, 0}, FlowSteps]

{{9/2, 9/2}, {27/11, 39/11}, {108/77, 200/77}, {72/77, 236/77}}

[see also SequenceFold]

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.