internal double GetValueTarget(HH hh, List <Person> tu, Person person = null)
        {
            double val;

            if (isTULevel)
            {
                val = hh.GetTUValue(index, tu);
            }
            else
            {
                if (person == null || target == DefPar.PREFIX.HEAD)
                {
                    val = hh.GetPersonValue(index, tu[0].indexInHH);
                }
                else if (target == DefPar.PREFIX.PARTNER)
                {
                    if (tu[0].partnerIndexInHH == byte.MaxValue)    // "no partner found! issue error" This is NOT the case in the old executable... for now, just give the Head value instead
                    {
                        //                        infoStore.communicator.ReportError(new Communicator.ErrorInfo()
                        //                        { isWarning = false, message = $"{description.Get()}: no partner found in assessment unit" });
                        val = double.NaN;
                    }
                    else
                    {
                        val = hh.GetPersonValue(index, tu[0].partnerIndexInHH);
                    }
                }
                else
                {
                    val = hh.GetPersonValue(index, person.indexInHH);
                }
            }
            return(val);
        }
        private void RunUnitLoop(HH hh, List <Person> tu)
        {
            // store the "running" variables (yem_unit, ils_earns_unit), and take the level into account
            foreach (StoreVar v in vars)
            {
                if (v.iteration != NOTAP)
                {
                    continue;                       // is handled below
                }
                if (hh.GetPersonValue(varIndexCurElig, tu[0].indexInHH) <= 0)
                {
                    continue;                                                         // store only for the (head of the) "currently elig" unit
                }
                List <Person> altTU  = hh.GetAlternativeTU(v.level, tu, description); // on may consider performance here, but let's first get it right
                double        levVal = v.unitLoopILPar == null?hh.GetTUValue(v.origVar.index, altTU) : v.unitLoopILPar.GetValue(hh, altTU);

                hh.SetPersonValue(levVal, v.storeVar.index, tu[0].indexInHH);
            }

            // store the "on demand" variables (e.g. yem_unit1) - they are stored the same way as for a normal loops (i.e. no level-stuff)
            foreach (StoreVar v in vars)
            {
                if (v.iteration != NOTAP && v.iteration == hh.GetPersonValue(varIndexLoopCounter, 0))
                {
                    hh.SetTUValue(hh.GetTUValue(v.origVar.index, tu), v.storeVar.index, tu);
                }
            }
        }
예제 #3
0
        void GetValues(HH hh, List <Person> tu, out OpItem selItem, out bool noAction)
        {
            double formulaOperandValue = 0; selItem = null; noAction = false;

            if (opTask == OPERAND_TYPE.FORMULA)
            {
                formulaOperandValue = formulaOperand.GetValue(hh, tu);
            }

            double max = double.MinValue, min = double.MaxValue;

            foreach (OpItem oi in opItems)
            {
                oi.numOperator = hh.GetPersonValue(oi.varOperator.index, tu[0].indexInHH);
                if (opTask == OPERAND_TYPE.FORMULA)
                {
                    oi.numOperand = formulaOperandValue;
                }
                if (opTask == OPERAND_TYPE.IL)
                {
                    oi.numOperand = hh.GetPersonValue(oi.varOperand.index, tu[0].indexInHH);
                }

                // remark: the behaviour of the old executable cannot be completely reproduced if there are equal mins/maxs
                // the old executable does the operation only on one item, but which depends on the order of content ...
                switch (selVar)
                {
                case DefPar.Value.ILVAROP_ALL: continue;

                case DefPar.Value.ILVAROP_MAX: if (oi.numOperator > max)
                    {
                        max = oi.numOperator; selItem = oi;
                    }
                    ; break;

                case DefPar.Value.ILVAROP_MIN: if (oi.numOperator < min)
                    {
                        min = oi.numOperator; selItem = oi;
                    }
                    ; break;

                case DefPar.Value.ILVAROP_MINPOS: if (oi.numOperator <= min && oi.numOperator > 0)
                    {
                        min = oi.numOperator; selItem = oi;
                    }
                    ; break;
                }
            }
            if (selVar == DefPar.Value.ILVAROP_MINPOS && selItem == null)
            {
                noAction = true;
            }
        }
        internal override double GetValue(HH hh, List <Person> tu, Person person)
        {
            double fatherId = hh.GetPersonValue(indexFatherIdVar, person.indexInHH);

            for (byte i = 0; i < hh.GetPersonCount(); i++)
            {
                if (fatherId == hh.GetPersonValue(indexPersonIdVar, i))
                {
                    return(parIncome.GetValue(hh, tu, new Person(i)));
                }
            }
            return(0.0);
        }
        internal override double GetValue(HH hh, List <Person> tu, Person person)
        {
            int           n = Convert.ToInt32(parN.GetValue()), m = Convert.ToInt32(parM.GetValue());
            List <Person> chs = (from p in tu where p.isDepChild select p).
                                OrderByDescending(p => hh.GetPersonValue(indexAgeVar, p.indexInHH)).
                                ThenBy(p => hh.GetPersonValue(indexPersonIdVar, p.indexInHH)).ToList();

            for (int i = Math.Max(n - 1, 0); i <= m - 1 && i < chs.Count; ++i)
            {
                if (person.indexInHH == chs[i].indexInHH)
                {
                    return(1);
                }
            }
            return(0.0);
        }
 protected double GetCounter(HH hh)
 {
     if (hh == null)
     {
         return(infoStore.hhAdmin.GlobalGetVar(varIndexLoopCounter)); // sequential run: get counter from 1st hh
     }
     return(hh.GetPersonValue(varIndexLoopCounter, 0));               // parallel run: get counter from 1st person of the currently processed hh
 }
        private static bool IsCondMetByTU(HH hh, List <Person> tu, string who, ParCond parCond = null, int iEligVar = -1)
        {
            int i = 0; if (who == DefPar.Value.WHO_NOBODY)

            {
                return(true);
            }

            if (parCond != null)
            {
                foreach (var pe in parCond.GetTUValues(hh, tu))
                {
                    bool elig = pe.Value; Person p = tu[i++];
                    bool?ew = EvalWho(elig, p); if (ew != null)
                    {
                        return(ew == true ? true : false);
                    }
                }
            }
            else
            {
                foreach (Person p in tu)
                {
                    bool elig = hh.GetPersonValue(iEligVar, p.indexInHH) != 0;
                    bool?ew   = EvalWho(elig, p); if (ew != null)
                    {
                        return(ew == true ? true : false);
                    }
                }
            }
            return(who == DefPar.Value.WHO_ALL || who == DefPar.Value.WHO_ALL_ADULTS);

            bool?EvalWho(bool elig, Person p)
            {
                if (who == DefPar.Value.WHO_ONE && elig)
                {
                    return(true);
                }
                if (who == DefPar.Value.WHO_ALL && !elig)
                {
                    return(false);
                }
                if (who == DefPar.Value.WHO_ONE_ADULT && !p.isDepChild && elig)
                {
                    return(true);
                }
                if ((who == DefPar.Value.WHO_ALL_ADULTS) && !p.isDepChild && !elig)
                {
                    return(false);
                }
                if (who == DefPar.Value.WHO_NOBODY)
                {
                    return(true);
                }
                return(null);
            }
        }
        internal override double GetValue(HH hh, List <Person> tu, Person person)
        {
            GetAgeLimits(out double ageMin, out double ageMax);
            double personId = hh.GetPersonValue(indexPersonIdVar, person.indexInHH);
            double income   = 0.0;

            for (byte i = 0; i < hh.GetPersonCount(); i++)
            {
                if (personId == hh.GetPersonValue(indexMotherIdVar, i) || personId == hh.GetPersonValue(indexFatherIdVar, i))
                {
                    double age = hh.GetPersonValue(indexAgeVar, i);
                    if (age >= ageMin && age <= ageMax)
                    {
                        income += parIncome.GetValue(hh, tu, new Person(i));
                    }
                }
            }
            return(income);
        }
        internal override double GetValue(HH hh, List <Person> tu, Person person)
        {
            GetAgeLimits(out double ageMin, out double ageMax);
            double headID = hh.GetPersonValue(indexPersonIdVar, person.indexInHH), partnerID = hh.GetPersonValue(indexPartnerIdVar, person.indexInHH);
            double n = 0.0;

            foreach (Person p in tu)
            {
                if (p.isDepChild || p.isLooseDepChild)
                {
                    if (hh.GetPersonValue(indexPersonIdVar, p.indexInHH) != headID && hh.GetPersonValue(indexPersonIdVar, p.indexInHH) != partnerID)
                    {
                        if (hh.GetPersonValue(indexFatherIdVar, p.indexInHH) == headID || (partnerID > 0 && hh.GetPersonValue(indexFatherIdVar, p.indexInHH) == partnerID) ||
                            hh.GetPersonValue(indexMotherIdVar, p.indexInHH) == headID || (partnerID > 0 && hh.GetPersonValue(indexMotherIdVar, p.indexInHH) == partnerID) ||
                            (hh.GetPersonValue(indexFatherIdVar, p.indexInHH) == 0 && hh.GetPersonValue(indexMotherIdVar, p.indexInHH) == 0))
                        {
                            if (hh.GetPersonValue(indexAgeVar, p.indexInHH) >= ageMin && hh.GetPersonValue(indexAgeVar, p.indexInHH) <= ageMax)
                            {
                                ++n;
                            }
                        }
                    }
                }
            }

            return(n);
        }
        private void RunLoopAndFix(HH hh, List <Person> tu)
        {
            // remark: gets an "invalid" loop-counter (0/end of loop) if a loop-store is used outside a loop, but that's a developer error/decision
            double curIteration = varIndexLoopCounter == NOTAP ? NOTAP : hh.GetPersonValue(varIndexLoopCounter, 0);

            foreach (StoreVar v in vars)
            {
                if (v.iteration == NOTAP ||      // always stored (possibly overwritten): yem_loop, yem_backup, i.e. in each iteration or outside any loop
                    v.iteration == curIteration) // stored only if it is the appropriate iteration: yem_loop7
                {
                    hh.SetTUValue(hh.GetTUValue(v.origVar.index, tu), v.storeVar.index, tu);
                }
            }
        }
예제 #11
0
        internal override double GetValue(HH hh, List <Person> tu, Person person)
        {
            double perId = hh.GetPersonValue(indexPersonIdVar, person.indexInHH);
            double parId = hh.GetPersonValue(indexPartnerIdVar, person.indexInHH);  // also get the partnerID

            if (parId == 0)
            {
                parId = -1;             // don't match 0!
            }
            for (byte i = 0; i < tu.Count; i++)
            {
                if (tu[i].isDepChild && (hh.GetPersonValue(indexFatherIdVar, tu[i].indexInHH) == perId || hh.GetPersonValue(indexMotherIdVar, tu[i].indexInHH) == perId ||
                                         hh.GetPersonValue(indexFatherIdVar, tu[i].indexInHH) == parId || hh.GetPersonValue(indexMotherIdVar, tu[i].indexInHH) == parId))
                {
                    return(1.0);
                }
            }
            if (person.isHead || person.isPartner)
            {
                return(tu.Count(p => p.isLooseDepChild && p.indexInHH != person.indexInHH) > 0 ? 1 : 0);
            }
            return(0.0);
        }
        internal override double GetValue(HH hh, List <Person> tu, Person person)
        {
            double n = 0.0;

            GetAgeLimits(out double ageMin, out double ageMax);
            foreach (Person p in tu)
            {
                double age = hh.GetPersonValue(indexAgeVar, p.indexInHH);
                if (age >= ageMin && age <= ageMax)
                {
                    ++n;
                }
            }
            return(n);
        }
        internal override double GetValue(HH hh, List <Person> tu, Person person)
        {
            double        personVal = minMaxVal.GetValue(hh, tu, person);
            List <Person> temp_tu   = tu;

            // first filter for adults if you have to
            if (minMaxAdultsOnly && temp_tu.Count(p => !p.isDepChild) > 0)
            {
                temp_tu = temp_tu.Where(p => !p.isDepChild).ToList();
            }
            // then order the remaining by val & id
            temp_tu = temp_tu.OrderBy(p => minMaxVal.GetValue(hh, tu, p)).
                      ThenBy(p => hh.GetPersonValue(indexPersonIdVar, p.indexInHH)).ToList();
            // then give value depending on unique
            return(((personVal == minMaxVal.GetValue(hh, tu, temp_tu[0])) && (!minMaxUnique || person == temp_tu[0])) ? 1.0 : 0.0);
        }
        // person != null: value is requested for a specific person
        // person == null: monetary: value is requested for tu, non-monetary: value is taken from head
        internal override double GetValue(HH hh, List <Person> tu, Person person = null)
        {
            if (alternativeTU != null)
            {
                tu = hh.GetAlternativeTU(alternativeTU, tu, description);
            }
            double val;

            if (target == DefPar.PREFIX.NONE)
            {
                val = person == null && isTULevel?hh.GetTUValue(index, tu)
                          : hh.GetPersonValue(index, person == null ? tu[0].indexInHH
                                                                                            : person.indexInHH);
            }
            else
            {
                val = GetValueTarget(hh, tu, person);
            }
            return(ApplyLimits(val, hh, tu, person));
        }
        private double getComponentValue(HH hh, List <Person> tu, Person person, List <Entry> component)
        {
            double value = 0;

            foreach (Entry entry in component)
            {
                double eVal;
                if (entry.isIL)
                {
                    eVal = getComponentValue(hh, tu, person, entry.ilEntries);
                }
                else
                {
                    eVal = person == null?hh.GetTUValue(entry.varSpec.index, tu)
                               : hh.GetPersonValue(entry.varSpec.index, person.indexInHH);
                }
                value += entry.addFactor * eVal;
            }
            return(value);
        }
        internal override double GetValue(HH hh, List <Person> tu, Person person)
        {
            double perId = hh.GetPersonValue(indexPersonIdVar, person.indexInHH);
            double parId = hh.GetPersonValue(indexPartnerIdVar, person.indexInHH);  // also get the partnerID

            if (parId == 0)
            {
                parId = -1;             // don't match 0!
            }
            for (int i = 0; i < hh.GetPersonCount(); i++)
            {
                if (hh.GetPersonValue(indexFatherIdVar, i) == perId || hh.GetPersonValue(indexMotherIdVar, i) == perId ||
                    hh.GetPersonValue(indexFatherIdVar, i) == parId || hh.GetPersonValue(indexMotherIdVar, i) == parId)
                {
                    return(1.0);
                }
            }
            return(0.0);
        }
예제 #17
0
        internal override double GetValue(HH hh, List <Person> tu, Person person)
        {
            GetAgeLimits(out double ageMin, out double ageMax);
            double headID = hh.GetPersonValue(indexPersonIdVar, person.indexInHH), partnerID = hh.GetPersonValue(indexPartnerIdVar, person.indexInHH);
            double n = 0.0;

            for (int i = 0; i < hh.GetPersonCount(); i++)
            {
                if (hh.GetPersonValue(indexPersonIdVar, i) != headID && hh.GetPersonValue(indexPersonIdVar, i) != partnerID)
                {
                    if (hh.GetPersonValue(indexFatherIdVar, i) == headID || (partnerID > 0 && hh.GetPersonValue(indexFatherIdVar, i) == partnerID) ||
                        hh.GetPersonValue(indexMotherIdVar, i) == headID || (partnerID > 0 && hh.GetPersonValue(indexMotherIdVar, i) == partnerID))
                    {
                        if (hh.GetPersonValue(indexAgeVar, i) >= ageMin && hh.GetPersonValue(indexAgeVar, i) <= ageMax)
                        {
                            ++n;
                        }
                    }
                }
            }

            return(n);
        }
 private double GetIterationsRequired(HH hh) // if we decide to implement the parameter Run_Once_If_No_Elig
 {                                           // one would ask whether this is false and then not do the Max
     return(Math.Max(1, hh.GetPersonValue(varIndexNElig, 0)));
 }
 internal override double GetValue(HH hh, List <Person> tu, Person person)
 {
     return((hh.GetPersonValue(indexPartnerIdVar, person.indexInHH) > 0.0) ? 1.0 : 0.0);
 }
 internal override double GetValue(HH hh, List <Person> tu, Person person)
 {
     return((hh.GetPersonValue(indexEducationVar, person.indexInHH) > 0.0 && hh.GetPersonValue(indexEconomicStatusVar, person.indexInHH) == 6.0) ? 1.0 : 0.0);
 }
        private void PopulateTUMembersInfo(List <Person> temp_tu, HH hh, double firstPersonIndexInHH, double headPersonID, bool produceWarnings = true)
        {
            Dictionary <byte, bool> allPartner = partnerCond.GetTUValues(hh, temp_tu, true);
            bool gotSinglePartner = false;  // this is used to allow only a single partner (unless "MultiplePartners" is "Allow")

            // first, calculate the query values for all members. This is not at all efficient, but it is what the old executable does. Making it more efficient would cut on available information.
            for (byte i = 0; i < temp_tu.Count; i++)
            {
                if (allPartner[temp_tu[i].indexInHH]) // the head (which is always first) can never be partner
                {
                    if (gotSinglePartner)             // only one partner is allowed and it was already found!
                    {
                        // if not "ignore" (i.e. if missing or "warn" - "allow" should never bring you to this path)
                        // parameter produceWarnings is also used here, to make sure you don't get the same warning twice, when looking for the head and when building the TU
                        if (produceWarnings && multiplePartners != DefPar.Value.MULTIPLE_PARTNERS_IGNORE)
                        {
                            infoStore.communicator.ReportError(new Communicator.ErrorInfo()
                            {
                                isWarning = true, message = $"{description.Get()}: more than one possible partner found for assessment unit '{tu_name}', idperson: '{hh.GetPersonValue(indexPersonId, temp_tu[i].indexInHH)}'"
                            });
                        }
                    }
                    else
                    {
                        temp_tu[i].isPartner = true;
                        gotSinglePartner     = multiplePartners != DefPar.Value.MULTIPLE_PARTNERS_ALLOW; // only set this to true if indeed only one partner is allowed
                        if (temp_tu[0].partnerIndexInHH == byte.MaxValue)
                        {
                            temp_tu[0].partnerIndexInHH = temp_tu[i].indexInHH;                                                 // but for the purposes of partner:yem, always keep the first partner found
                        }
                    }
                }
                else
                {
                    temp_tu[i].isPartner = false;
                }
            }
            // we need to get each condition in turn and pass the values to the members
            //(ideally we would be able to have a single loop and get all conditions of first person, then second etc. but this is not possible with GetTUValues)
            Dictionary <byte, bool> allOwnChild = ownChildCond.GetTUValues(hh, temp_tu, true);

            for (byte i = 0; i < temp_tu.Count; i++)
            {
                temp_tu[i].isOwnChild = allOwnChild[temp_tu[i].indexInHH];
            }

            Dictionary <byte, bool> allDepChild = depChildCond.GetTUValues(hh, temp_tu, true);

            for (byte i = 0; i < temp_tu.Count; i++)
            {
                temp_tu[i].isDepChild = (!noChildIfHead || !temp_tu[i].isHead) && (!noChildIfPartner || !temp_tu[i].isPartner) && allDepChild[temp_tu[i].indexInHH];
            }

            Dictionary <byte, bool> allOwnDepChild = ownDepChildCond.GetTUValues(hh, temp_tu, true);

            for (byte i = 0; i < temp_tu.Count; i++)
            {
                temp_tu[i].isOwnDepChild = allOwnDepChild[temp_tu[i].indexInHH];
            }

            Dictionary <byte, bool> allDepParent = depParentCond.GetTUValues(hh, temp_tu, true);

            for (byte i = 0; i < temp_tu.Count; i++)
            {
                temp_tu[i].isDepParent = allDepParent[temp_tu[i].indexInHH];
            }

            Dictionary <byte, bool> allLooseDepChild = looseDepChildCond.GetTUValues(hh, temp_tu, true);

            for (byte i = 0; i < temp_tu.Count; i++)
            {
                temp_tu[i].isLooseDepChild = allLooseDepChild[temp_tu[i].indexInHH];
            }

            Dictionary <byte, bool> allDepRelative = depRelativeCond.GetTUValues(hh, temp_tu, true);

            for (byte i = 0; i < temp_tu.Count; i++)
            {
                temp_tu[i].isDepRelative = allDepRelative[temp_tu[i].indexInHH];
            }

            // note: we need to run loneParentCond only on people already added to the TU! so it cannot be done here

            for (byte i = 0; i < temp_tu.Count; i++)
            {
                temp_tu[i].tuHeadID = headPersonID;
            }
        }
        internal override void Run(HH hh, List <Person> tu) // is called for each person in HH, i.e. with individual TU
        {
            if (!IsRunCondMet(hh))
            {
                return;
            }

            // 1st step of uprating aggregates: store original values
            Dictionary <int, List <double> > orParts  = new Dictionary <int, List <double> >(); // store the original parts ...
            Dictionary <int, double>         sumParts = new Dictionary <int, double>();         // ... and the actual sum of the parts (which may differ from the aggregate, hopefully not by much)

            foreach (AggUpVar auv in aggUpVars)
            {
                double agg  = hh.GetPersonValue(auv.varSpec.index, tu[0].indexInHH);
                double sumP = 0.0;
                foreach (VarSpec pv in auv.parts)
                {
                    double p = hh.GetPersonValue(pv.index, tu[0].indexInHH); sumP += p;
                    if (!orParts.ContainsKey(auv.varSpec.index))
                    {
                        orParts.Add(auv.varSpec.index, new List <double>());
                    }
                    orParts[auv.varSpec.index].Add(p);
                }
                //sumParts.Add(auv.varSpec.index, sumP); // first thought that this approach is more accurate ...
                sumParts.Add(auv.varSpec.index, agg);    // ... but the old exe uses this
                if (Math.Abs(agg - sumP) > auv.tolerance)
                {
                    infoStore.communicator.ReportError(new Communicator.ErrorInfo()
                    {
                        isWarning      = true,
                        runTimeErrorId = description.funID,
                        message        = $"{description.Get()}: idperson {infoStore.GetIDPerson(hh, tu[0].indexInHH)}: parts of {auv.varSpec.name} do not sum up {agg} <> {sumP}"
                    });
                }
            }

            // uprating "normal" variables
            foreach (UpVar uv in upVars)
            {
                if (uv.parCondition == null || // either there is no condition or the condition is fulfilled
                    uv.parCondition.GetPersonValue(hh, tu[0]))
                {
                    hh.SetPersonValue(hh.GetPersonValue(uv.varSpec.index, tu[0].indexInHH) * uv.GetFactor(isDBYearVarUsed ? hh.GetPersonValue(indDBYearVar, tu[0].indexInHH) : UpVar.ALLYEARS),
                                      uv.varSpec.index, tu[0].indexInHH);
                }
            }
            // 2nd step of uprating aggregates: build the factor of each aggregate variable as
            // factor_part1 * share_part1 + ... + factor_partN * share_partN =
            // = (part1_new / part1_old) * (part1_old / sum_parts_old) + ... + (partN_new / partN_old) * (partN_old / sum_parts_old)
            foreach (AggUpVar auv in aggUpVars)
            {
                if (sumParts[auv.varSpec.index] == 0)
                {
                    continue;                                   // avoid division by zero
                }
                double agg = hh.GetPersonValue(auv.varSpec.index, tu[0].indexInHH); double factor = 0;
                for (int i = 0; i < auv.parts.Count; ++i)
                {
                    if (orParts[auv.varSpec.index][i] == 0)
                    {
                        continue;                                     // avoid division by zero
                    }
                    double newPart = hh.GetPersonValue(auv.parts[i].index, tu[0].indexInHH);
                    factor += (newPart / orParts[auv.varSpec.index][i]) * (orParts[auv.varSpec.index][i] / sumParts[auv.varSpec.index]);
                }
                hh.SetPersonValue(agg * factor, auv.varSpec.index, tu[0].indexInHH);
            }
        }
 internal double GetIDPerson(HH hh, int personIndex)
 {
     return(hh.GetPersonValue(operandAdmin.GetIndexInPersonVarList(DefVarName.IDPERSON), personIndex));
 }
 internal double GetIDHH(HH hh)
 {
     return(hh.GetPersonValue(operandAdmin.GetIndexInPersonVarList(DefVarName.IDHH), 0));
 }
예제 #25
0
        internal override double GetValue(HH hh, List <Person> tu, Person person)
        {
            double occ = hh.GetPersonValue(indexOccupationVar, person.indexInHH);

            return((occ == 0 || occ == 6 || occ == 7 || occ == 8 || occ == 9) ? 1.0 : 0.0);
        }
 internal override double GetValue(HH hh, List <Person> tu, Person person)
 {
     return((hh.GetPersonValue(indexCivilServantVar, person.indexInHH) == 1.0) ? 1.0 : 0.0);
 }
 internal override double GetValue(HH hh, List <Person> tu, Person person)
 {
     return((hh.GetPersonValue(indexMaritalStatus, person.indexInHH) == 2.0) ? 1.0 : 0.0);
 }
        // This is the function that composes the actual TUs
        internal List <List <Person> > ComposeTUs(HH hh, out bool _isStatic)
        {
            // first check if we need to initiate the local objects
            PrepareLocals();

            List <List <Person> > tus = new List <List <Person> >();

            _isStatic = isStatic; // if static, TUs can be stored and do not need to be recreated on next use

            // if type is INDIVIDUAL, then create a TU for each person
            if (tuType == DefPar.Value.TUTYPE_IND)
            {
                for (byte i = 0; i < hh.GetPersonCount(); ++i)
                {
                    double        headPersonID = hh.GetPersonValue(indexPersonId, i);
                    List <Person> temp_tu      = new List <Person>()
                    {
                        new Person(i)
                        {
                            tuHeadID = headPersonID, isHead = true
                        }
                    };
                    PopulateTUMembersInfo(temp_tu, hh, 0, headPersonID);    // isLoneParent is not filled, but is impossible in Individual households anyway...
                    tus.Add(temp_tu);
                }
                return(tus);
            }
            else // else type is HOUSEHOLD or SUBGROUP
            {
                // compose a dictionary of all members, to hold calculated incomes & ages
                Dictionary <byte, Person> headCandidates = new Dictionary <byte, Person>();
                for (byte i = 0; i < hh.personVarList.Count; i++)
                {
                    headCandidates.Add(i, new Person(i));
                }

                if (tuType == DefPar.Value.TUTYPE_SUBGROUP) // if type is SUBGROUP, create TUs until you run out of HH members
                {
                    while (headCandidates.Count > 0)
                    {
                        byte          head         = GetHead(hh, headCandidates); // first get the head from the unassigned HH members
                        double        headPersonID = hh.GetPersonValue(indexPersonId, head);
                        List <Person> tu           = new List <Person>();

                        // create a TU with all unassigned members and this head - this will be used for calculating Person attributes (e.g. isDepChild etc)
                        List <Person> temp_tu = MakeTempTU(headCandidates, hh, head);

                        // populate the Person attributes, except for isLoneParent (see below)
                        PopulateTUMembersInfo(temp_tu, hh, headCandidates.ElementAt(0).Key, headPersonID);

                        // add the head to the new TU
                        tu.Add(temp_tu[0]);     // head should always be first
                        temp_tu.RemoveAt(0);    // remove it from the temp_tu

                        // add the rest of the members
                        foreach (string member_type in members)
                        {
                            List <Person> ps = new List <Person>();
                            switch (member_type)
                            {
                            case DefPar.DefTu.MEMBER_TYPE.DEPCHILD:
                                ps = temp_tu.Where(p => p.isDepChild).ToList(); break;

                            case DefPar.DefTu.MEMBER_TYPE.DEPPARENT:
                                ps = temp_tu.Where(p => p.isDepParent).ToList(); break;

                            case DefPar.DefTu.MEMBER_TYPE.DEPRELATIVE:
                                ps = temp_tu.Where(p => p.isDepRelative).ToList(); break;

                            case DefPar.DefTu.MEMBER_TYPE.LOOSEDEPCHILD:
                                ps = temp_tu.Where(p => p.isLooseDepChild).ToList(); break;

                            case DefPar.DefTu.MEMBER_TYPE.OWNCHILD:
                                ps = temp_tu.Where(p => p.isOwnChild).ToList(); break;

                            case DefPar.DefTu.MEMBER_TYPE.OWNDEPCHILD:
                                ps = temp_tu.Where(p => p.isOwnDepChild).ToList(); break;

                            case DefPar.DefTu.MEMBER_TYPE.PARTNER:
                                ps = temp_tu.Where(p => p.isPartner).ToList(); break;

                            default:
                                infoStore.communicator.ReportError(new Communicator.ErrorInfo()
                                {
                                    isWarning = false, runTimeErrorId = description.Get(), message = $"{description.Get()}: invalid member type found '{member_type}'"
                                });
                                break;
                            }
                            foreach (Person p in ps)
                            {
                                tu.Add(p);
                                temp_tu.Remove(p);
                            }
                        }

                        // then, if required, add the DepOfDep
                        if (assignPartnerOfDependents || assignDepChOfDependents)
                        {
                            // in the old executable we only check remaining members in order
                            // in EM3, should we allow members to be added in mixed order too?
                            byte i = 0;
                            // for each member of the TU
                            while (i < tu.Count)
                            {
                                // if this person is a dependent, check if members can be attached
                                if (tu[i].isDepChild || tu[i].isDepParent || tu[i].isDepRelative || tu[i].isLooseDepChild || tu[i].isOwnDepChild || tu[i].isDepMember)
                                {
                                    double personId = hh.GetPersonValue(indexPersonId, tu[i].indexInHH);
                                    int    j        = 0;
                                    // check the unassigned members
                                    while (j < temp_tu.Count)
                                    {
                                        bool add = false;
                                        if (assignPartnerOfDependents && (personId == hh.GetPersonValue(indexPartnerId, temp_tu[j].indexInHH)))
                                        {
                                            add = true;
                                        }
                                        else if (assignDepChOfDependents && temp_tu[j].isDepChild &&
                                                 (personId == hh.GetPersonValue(indexFatherId, temp_tu[j].indexInHH) ||
                                                  personId == hh.GetPersonValue(indexMotherId, temp_tu[j].indexInHH)))
                                        {
                                            add = true;
                                        }
                                        if (add)
                                        {
                                            temp_tu[j].isDepMember = true;  // flag this as a dependent member
                                            tu.Add(temp_tu[j]);             // add it to the TU
                                            temp_tu.RemoveAt(j);            // and remove it from the unassigned
                                        }
                                        else
                                        {
                                            j++;     // advance to the next unassigned member
                                        }
                                    }
                                }
                                ++i;    // advance to the next person in the TU
                            }
                        }

                        // we need to run loneParentCond only on people already added to the TU!
                        Dictionary <byte, bool> allLoneParent = loneParentCond.GetTUValues(hh, tu, true);
                        for (byte i = 0; i < tu.Count; i++)
                        {
                            tu[i].isLoneParent = allLoneParent[tu[i].indexInHH];
                        }

                        // the TU is complete, so add it to the list of TUs
                        tus.Add(tu);

                        // finally remove anyone that was added in this TU from the headCandidates of the next round
                        foreach (Person p in tu)
                        {
                            headCandidates.Remove(p.indexInHH);
                        }
                    }
                }
                else
                {
                    byte          head         = GetHead(hh, headCandidates);
                    double        headPersonID = hh.GetPersonValue(indexPersonId, head);
                    List <Person> tu           = new List <Person>();
                    tu.Add(new Person(head)
                    {
                        isHead = true, tuHeadID = headPersonID
                    });                                             // head should always be first
                    for (byte i = 0; i < hh.GetPersonCount(); ++i)  // then add the rest
                    {
                        if (i != head)
                        {
                            tu.Add(new Person(i));
                        }
                    }
                    PopulateTUMembersInfo(tu, hh, tu[0].indexInHH, headPersonID);
                    // we need to run loneParentCond only on people already added to the TU!
                    Dictionary <byte, bool> allLoneParent = loneParentCond.GetTUValues(hh, tu, true);
                    for (byte i = 0; i < tu.Count; i++)
                    {
                        tu[i].isLoneParent = allLoneParent[tu[i].indexInHH];
                    }
                    tus.Add(tu);
                }
            }
            return(tus);
        }
        private byte GetHead(HH hh, Dictionary <byte, Person> headCandidates)
        {
            List <Person> heads = new List <Person>();
            // first keep only candidates that satisfy the ExtHeadCond

            // get temp HH values for the head condition, assuming the first candidate as the head
            List <Person> temp_tu = headCandidates.Values.ToList();

            PopulateTUMembersInfo(temp_tu, hh, temp_tu[0].indexInHH, hh.GetPersonValue(indexPersonId, temp_tu[0].indexInHH), false);

            Dictionary <byte, bool> headElig = extHeadCond.GetTUValues(hh, temp_tu);

            foreach (byte i in headCandidates.Keys)
            {
                if (headElig[i])
                {
                    if (!headCandidates[i].headCalculation.calculated)
                    {
                        headCandidates[i].headCalculation.calculated = true;
                        headCandidates[i].headCalculation.income     = headDefInc.GetValue(hh, new List <Person>()
                        {
                            new Person((byte)i)
                        });
                        headCandidates[i].headCalculation.age = hh.personVarList[i][indexDag];
                    }
                    heads.Add(headCandidates[i]);
                }
            }
            if (heads.Count == 0)   // if no candidate passed, then depending on StopIfNoHeadFound, either issue an error, or repeat the above, ignoring ExtHeadCond
            {
                if (stopIfNoHeadFound)
                {
                    double idhh = hh.personVarList[0][infoStore.operandAdmin.GetIndexInPersonVarList(DefVarName.IDHH)];
                    infoStore.communicator.ReportError(new Communicator.ErrorInfo()
                    {
                        isWarning = false, message = $"{description.Get()}: Household {idhh}: extended head conidtion '{extHeadCond.xmlValue}' rules out all assessment unit members for being head"
                    });
                }
                else
                {
                    foreach (byte i in headCandidates.Keys)
                    {
                        if (!headCandidates[i].headCalculation.calculated)
                        {
                            headCandidates[i].headCalculation.calculated = true;
                            headCandidates[i].headCalculation.income     = headDefInc.GetValue(hh, new List <Person>()
                            {
                                new Person((byte)i)
                            });
                            headCandidates[i].headCalculation.age = hh.personVarList[i][indexDag];
                        }
                        heads.Add(headCandidates[i]);
                    }
                }
            }
            // if more than one individuals satisfy the ExtHeadCond, check the income
            if (heads.Count > 1)
            {
                double maxInc = heads.Select(x => x.headCalculation.income).Max();   // get the max income
                for (int i = heads.Count - 1; i >= 0; i--)
                {
                    if (heads[i].headCalculation.income < maxInc)
                    {
                        heads.RemoveAt(i);                                           // and remove anyone poorer
                    }
                }
            }
            // if more than one individuals have the same income, check the age
            if (heads.Count > 1)
            {
                double maxAge = heads.Select(x => x.headCalculation.age).Max();   // get the max age
                for (int i = heads.Count - 1; i >= 0; i--)
                {
                    if (heads[i].headCalculation.age < maxAge)
                    {
                        heads.RemoveAt(i);                                        // and remove anyone younger
                    }
                }
            }
            // if no head found, return error
            if (heads.Count == 0)
            {
                double idhh = hh.personVarList[0][infoStore.operandAdmin.GetIndexInPersonVarList(DefVarName.IDHH)];
                infoStore.communicator.ReportError(new Communicator.ErrorInfo()
                {
                    isWarning = false, message = $"{description.Get()}: Could not determine a valid head for hh {idhh}"
                });
            }
            // else return the first head found
            return(heads[0].indexInHH);
        }