private void RunModel(ChoiceProbabilityCalculator choiceProbabilityCalculator, IPersonDayWrapper personDay, DayPattern choice = null)
        {
            IHouseholdWrapper household       = personDay.Household;
            IParcelWrapper    residenceParcel = household.ResidenceParcel;
            IPersonWrapper    person          = personDay.Person;

            int    carsPerDriver       = household.GetCarsPerDriver();
            double mixedDensity        = residenceParcel.MixedUse3Index2();
            double intersectionDensity = residenceParcel.IntersectionDensity34Minus1Buffer2();

            double[] purposeLogsums = new double[Global.Settings.Purposes.TotalPurposes];
            double[] atUsualLogsums = new double[3];
            int      carOwnership   = person.GetCarOwnershipSegment();
            int      votSegment     = person.Household.GetVotALSegment();
            int      transitAccess  = residenceParcel.TransitAccessSegment();

            if (person.UsualWorkParcel == null || person.UsualWorkParcelId == household.ResidenceParcelId)
            {
                purposeLogsums[Global.Settings.Purposes.Work] = 0;
            }
            else
            {
                int destinationArrivalTime   = ChoiceModelUtility.GetDestinationArrivalTime(Global.Settings.Models.WorkTourModeModel);
                int destinationDepartureTime = ChoiceModelUtility.GetDestinationDepartureTime(Global.Settings.Models.WorkTourModeModel);
                ChoiceProbabilityCalculator.Alternative nestedAlternative = Global.ChoiceModelSession.Get <WorkTourModeModel>().RunNested(personDay, residenceParcel, person.UsualWorkParcel, destinationArrivalTime, destinationDepartureTime, household.VehiclesAvailable);

                purposeLogsums[Global.Settings.Purposes.Work] = nestedAlternative == null ? 0 : nestedAlternative.ComputeLogsum();
                atUsualLogsums[Global.Settings.Purposes.Work] = Global.AggregateLogsums[person.UsualWorkParcel.ZoneId][Global.Settings.Purposes.HomeBasedComposite][carOwnership][votSegment][person.UsualWorkParcel.TransitAccessSegment()];
            }

            if (person.UsualSchoolParcel == null || person.UsualSchoolParcelId == household.ResidenceParcelId)
            {
                purposeLogsums[Global.Settings.Purposes.School] = 0;
            }
            else
            {
                int destinationArrivalTime   = ChoiceModelUtility.GetDestinationArrivalTime(Global.Settings.Models.SchoolTourModeModel);
                int destinationDepartureTime = ChoiceModelUtility.GetDestinationDepartureTime(Global.Settings.Models.SchoolTourModeModel);
                ChoiceProbabilityCalculator.Alternative nestedAlternative = Global.ChoiceModelSession.Get <SchoolTourModeModel>().RunNested(personDay, residenceParcel, person.UsualSchoolParcel, destinationArrivalTime, destinationDepartureTime, household.VehiclesAvailable);

                purposeLogsums[Global.Settings.Purposes.School] = nestedAlternative == null ? 0 : nestedAlternative.ComputeLogsum();
                atUsualLogsums[Global.Settings.Purposes.School] = Global.AggregateLogsums[person.UsualSchoolParcel.ZoneId][Global.Settings.Purposes.HomeBasedComposite][carOwnership][votSegment][person.UsualSchoolParcel.TransitAccessSegment()];
            }

            double compositeLogsum = Global.AggregateLogsums[household.ResidenceZoneId][Global.Settings.Purposes.HomeBasedComposite][carOwnership][votSegment][transitAccess];

            purposeLogsums[Global.Settings.Purposes.Escort]           = Global.AggregateLogsums[household.ResidenceZoneId][Global.Settings.Purposes.Escort][carOwnership][votSegment][transitAccess];
            purposeLogsums[Global.Settings.Purposes.PersonalBusiness] = Global.AggregateLogsums[household.ResidenceZoneId][Global.Settings.Purposes.PersonalBusiness][carOwnership][votSegment][transitAccess];
            purposeLogsums[Global.Settings.Purposes.Shopping]         = Global.AggregateLogsums[household.ResidenceZoneId][Global.Settings.Purposes.Shopping][carOwnership][votSegment][transitAccess];
            purposeLogsums[Global.Settings.Purposes.Meal]             = Global.AggregateLogsums[household.ResidenceZoneId][Global.Settings.Purposes.Meal][carOwnership][votSegment][transitAccess];
            purposeLogsums[Global.Settings.Purposes.Social]           = Global.AggregateLogsums[household.ResidenceZoneId][Global.Settings.Purposes.Social][carOwnership][votSegment][transitAccess];
            purposeLogsums[Global.Settings.Purposes.Recreation]       = compositeLogsum;
            purposeLogsums[Global.Settings.Purposes.Medical]          = compositeLogsum;

            for (int xPurpose = Global.Settings.Purposes.Work; xPurpose <= Global.Settings.Purposes.Social + 10; xPurpose++)
            {
                // extra components 1-5 are for 2,3,4,5,6 tour purposes
                // extra components 6-10 are for 2,3,4,5,6 stop puroposes

                // recode purpose to match coefficients
                int purpose = xPurpose <= Global.Settings.Purposes.Social ? xPurpose :
                              xPurpose <= Global.Settings.Purposes.Social + 5 ?
                              Global.Settings.Purposes.Social + 1 : Global.Settings.Purposes.Social + 2;

                // get correct multiplier on coefficients.
                double xMultiplier = xPurpose <= Global.Settings.Purposes.Social ? 1.0 :
                                     xPurpose <= Global.Settings.Purposes.Social + 5 ?
                                     Math.Log(xPurpose - Global.Settings.Purposes.Social + 1) : Math.Log(xPurpose - Global.Settings.Purposes.Social - 5 + 1);

                choiceProbabilityCalculator.CreateUtilityComponent(xPurpose);
                ChoiceProbabilityCalculator.Component component = choiceProbabilityCalculator.GetUtilityComponent(xPurpose);

                component.AddUtilityTerm(100 * purpose + 51, xMultiplier * person.IsFulltimeWorker.ToFlag());
                component.AddUtilityTerm(100 * purpose + 2, xMultiplier * person.IsPartTimeWorker.ToFlag());
                component.AddUtilityTerm(100 * purpose + 3, xMultiplier * person.IsRetiredAdult.ToFlag());
                component.AddUtilityTerm(100 * purpose + 4, xMultiplier * person.IsNonworkingAdult.ToFlag());
                component.AddUtilityTerm(100 * purpose + 5, xMultiplier * person.IsUniversityStudent.ToFlag());
                component.AddUtilityTerm(100 * purpose + 6, xMultiplier * person.IsDrivingAgeStudent.ToFlag());
                component.AddUtilityTerm(100 * purpose + 7, xMultiplier * person.IsChildAge5Through15.ToFlag());
                component.AddUtilityTerm(100 * purpose + 8, xMultiplier * person.IsChildUnder5.ToFlag());
                component.AddUtilityTerm(100 * purpose + 9, xMultiplier * household.Has0To25KIncome.ToFlag());
                component.AddUtilityTerm(100 * purpose + 10, xMultiplier * household.Has25To45KIncome.ToFlag());
                component.AddUtilityTerm(100 * purpose + 11, xMultiplier * household.Has75KPlusIncome.ToFlag());
                component.AddUtilityTerm(100 * purpose + 12, xMultiplier * carsPerDriver);
                component.AddUtilityTerm(100 * purpose + 13, xMultiplier * person.IsOnlyAdult().ToFlag());
                component.AddUtilityTerm(100 * purpose + 14, xMultiplier * person.IsOnlyFullOrPartTimeWorker().ToFlag());
                component.AddUtilityTerm(100 * purpose + 15, xMultiplier * 0);
                component.AddUtilityTerm(100 * purpose + 16, xMultiplier * person.IsFemale.ToFlag() * person.IsAdult.ToFlag() * (!household.HasChildrenUnder16).ToFlag());
                component.AddUtilityTerm(100 * purpose + 17, xMultiplier * person.IsFemale.ToFlag() * person.IsAdult.ToFlag() * household.HasChildrenUnder5.ToFlag());
                component.AddUtilityTerm(100 * purpose + 18, xMultiplier * person.IsFemale.ToFlag() * person.IsAdult.ToFlag() * household.HasChildrenAge5Through15.ToFlag());
                component.AddUtilityTerm(100 * purpose + 19, xMultiplier * person.IsMale.ToFlag() * person.IsAdult.ToFlag() * household.HasChildrenUnder5.ToFlag());
                component.AddUtilityTerm(100 * purpose + 20, xMultiplier * person.IsMale.ToFlag() * person.IsAdult.ToFlag() * household.HasChildrenAge5Through15.ToFlag());
                component.AddUtilityTerm(100 * purpose + 21, xMultiplier * person.AgeIsBetween18And25.ToFlag());
                component.AddUtilityTerm(100 * purpose + 22, xMultiplier * person.AgeIsBetween26And35.ToFlag());
                component.AddUtilityTerm(100 * purpose + 23, xMultiplier * person.AgeIsBetween51And65.ToFlag());
                component.AddUtilityTerm(100 * purpose + 24, xMultiplier * person.WorksAtHome().ToFlag());
                component.AddUtilityTerm(100 * purpose + 25, xMultiplier * mixedDensity);
                component.AddUtilityTerm(100 * purpose + 26, xMultiplier * intersectionDensity);
                component.AddUtilityTerm(100 * purpose + 27, xMultiplier * purposeLogsums[purpose]);
                component.AddUtilityTerm(100 * purpose + 28, xMultiplier * person.TransitPassOwnership);
            }

            // tour utility
            int tourComponentIndex = Global.Settings.Purposes.Social + 11;

            choiceProbabilityCalculator.CreateUtilityComponent(tourComponentIndex);
            ChoiceProbabilityCalculator.Component tourComponent = choiceProbabilityCalculator.GetUtilityComponent(tourComponentIndex);
            tourComponent.AddUtilityTerm(1401, carsPerDriver);
            tourComponent.AddUtilityTerm(1402, person.WorksAtHome().ToFlag());
            tourComponent.AddUtilityTerm(1403, mixedDensity);
            tourComponent.AddUtilityTerm(1404, mixedDensity * person.IsChildAge5Through15.ToFlag());
            tourComponent.AddUtilityTerm(1405, compositeLogsum);
            tourComponent.AddUtilityTerm(1406, person.TransitPassOwnership);

            // stop utility
            int stopComponentIndex = Global.Settings.Purposes.Social + 12;

            choiceProbabilityCalculator.CreateUtilityComponent(stopComponentIndex);
            ChoiceProbabilityCalculator.Component stopComponent = choiceProbabilityCalculator.GetUtilityComponent(stopComponentIndex);
            stopComponent.AddUtilityTerm(1411, carsPerDriver);
            stopComponent.AddUtilityTerm(1412, person.WorksAtHome().ToFlag());
            stopComponent.AddUtilityTerm(1413, mixedDensity);
            stopComponent.AddUtilityTerm(1414, mixedDensity * person.IsChildAge5Through15.ToFlag());
            stopComponent.AddUtilityTerm(1415, compositeLogsum);
            stopComponent.AddUtilityTerm(1416, person.TransitPassOwnership);

            for (int alternativeIndex = 0; alternativeIndex < TOTAL_ALTERNATIVES; alternativeIndex++)
            {
                DayPattern dayPattern = _dayPatterns[alternativeIndex];
                bool       available  =
                    // work tours and stops only available for workers
                    (person.IsWorker || (dayPattern.WorkTours <= 0 && dayPattern.WorkStops <= 0)) &&
                    // school tours and stops only available for students with usual school parcel not at home
                    ((person.IsStudent && person.UsualSchoolParcel != null && person.UsualSchoolParcel != person.Household.ResidenceParcel) || (dayPattern.SchoolTours <= 0 && dayPattern.SchoolStops <= 0)) &&
                    // school stops not available if usual school parcel is same as usual work parcel
                    ((person.IsStudent && person.UsualSchoolParcel != null && person.UsualSchoolParcel != person.UsualWorkParcel) || (dayPattern.SchoolStops <= 0));

                ChoiceProbabilityCalculator.Alternative alternative = choiceProbabilityCalculator.GetAlternative(alternativeIndex, available, choice != null && choice.Equals(dayPattern));

                if (!Global.Configuration.IsInEstimationMode && !alternative.Available)
                {
                    continue;
                }

                alternative.Choice = dayPattern;

                // components for the purposes
                for (int purpose = Global.Settings.Purposes.Work; purpose <= Global.Settings.Purposes.Social; purpose++)
                {
                    if (dayPattern.Tours[purpose] > 0 || dayPattern.Stops[purpose] > 0)
                    {
                        alternative.AddUtilityComponent(choiceProbabilityCalculator.GetUtilityComponent(purpose));

                        if (dayPattern.Tours[purpose] > 0)
                        {
                            alternative.AddUtilityTerm(100 * purpose, 1);                                  // tour purpose ASC
                            alternative.AddUtilityTerm(100 * purpose + 29, purposeLogsums[purpose]);       // tour purpose logsum
                            alternative.AddUtilityTerm(100 * purpose + 30, person.PaidParkingAtWorkplace); // only use for work purpose
                        }

                        if (dayPattern.Stops[purpose] > 0)
                        {
                            alternative.AddUtilityTerm(100 * purpose + 1, 1);                        // stop purpose ASC
                            alternative.AddUtilityTerm(100 * purpose + 31, purposeLogsums[purpose]); // stop purpose logsum
                        }
                        if (Global.Configuration.IsInEstimationMode)
                        {
                            alternative.AddUtilityTerm(100 * purpose + 32, 1 - person.PaperDiary);
                            alternative.AddUtilityTerm(100 * purpose + 33, person.ProxyResponse);
                        }
                    }
                }

                // multiple tour purposes component
                if (dayPattern.TotalTours > 1)
                {
                    alternative.AddUtilityComponent(choiceProbabilityCalculator.GetUtilityComponent(Global.Settings.Purposes.Social + (dayPattern.TotalTours - 1)));
                }

                // multiple stop purposes component
                if (dayPattern.TotalStops > 1)
                {
                    alternative.AddUtilityComponent(choiceProbabilityCalculator.GetUtilityComponent(Global.Settings.Purposes.Social + 5 + (dayPattern.TotalStops - 1)));
                }

                for (int tourPurpose = Global.Settings.Purposes.Work; tourPurpose <= Global.Settings.Purposes.Social; tourPurpose++)
                {
                    for (int stopPurpose = Global.Settings.Purposes.Work; stopPurpose <= Global.Settings.Purposes.Social - 1; stopPurpose++)
                    {
                        if (tourPurpose > Global.Settings.Purposes.School && stopPurpose <= Global.Settings.Purposes.School)
                        {
                            continue;
                        }

                        if (dayPattern.Tours[tourPurpose] > 0 && dayPattern.Stops[stopPurpose] > 0)
                        {
                            alternative.AddUtilityTerm(1000 + 10 * tourPurpose + stopPurpose, 1); // tour-stop comb. utility
                        }
                    }
                }

                for (int tourPurpose = Global.Settings.Purposes.Work; tourPurpose <= Global.Settings.Purposes.School; tourPurpose++)
                {
                    if (dayPattern.Tours[tourPurpose] == 1 && dayPattern.TotalStops >= 1)
                    {
                        alternative.AddUtilityTerm(1000 + 10 * tourPurpose, purposeLogsums[tourPurpose]);     // usual location logsum x presence of stops in work or school pattern
                        alternative.AddUtilityTerm(1000 + 10 * tourPurpose + 8, compositeLogsum);             // home aggregate logsum x  presence of stops in work or school pattern
                        alternative.AddUtilityTerm(1000 + 10 * tourPurpose + 9, atUsualLogsums[tourPurpose]); // at usual location aggregate logsum x  presence of stops in work or school pattern
                    }
                }

                for (int tourPurpose = Global.Settings.Purposes.Work; tourPurpose <= Global.Settings.Purposes.Social - 2; tourPurpose++)
                {
                    for (int tourPurpose2 = tourPurpose + 1; tourPurpose2 <= Global.Settings.Purposes.Social; tourPurpose2++)
                    {
                        if (dayPattern.Tours[tourPurpose] > 0 && dayPattern.Tours[tourPurpose2] > 0)
                        {
                            alternative.AddUtilityTerm(1100 + 10 * tourPurpose + tourPurpose2, 1); // tour-tour comb. utility
                        }
                    }
                }

                for (int stopPurpose = Global.Settings.Purposes.Work; stopPurpose <= Global.Settings.Purposes.Social - 2; stopPurpose++)
                {
                    for (int stopPurpose2 = stopPurpose + 1; stopPurpose2 <= Global.Settings.Purposes.Social; stopPurpose2++)
                    {
                        if (dayPattern.Stops[stopPurpose] > 0 && dayPattern.Stops[stopPurpose2] > 0)
                        {
                            alternative.AddUtilityTerm(1200 + 10 * stopPurpose + stopPurpose2, 1); // stop-stop comb. utility
                        }
                    }
                }

                if (dayPattern.TotalTours > 0 && dayPattern.TotalStops > 0)
                {
                    int totalStops = dayPattern.TotalStops;

                    if (totalStops > 3)
                    {
                        totalStops = 3;
                    }

                    alternative.AddUtilityTerm(1300 + 10 * dayPattern.TotalTours + totalStops, 1); // nttour-ntstop utility
                }
                if (dayPattern.TotalTours - dayPattern.Tours[Global.Settings.Purposes.Work] - dayPattern.Tours[Global.Settings.Purposes.School] > 0)
                {
                    // tour utility
                    alternative.AddUtilityComponent(choiceProbabilityCalculator.GetUtilityComponent(tourComponentIndex));
                }
                if (dayPattern.TotalStops - dayPattern.Stops[Global.Settings.Purposes.Work] - dayPattern.Stops[Global.Settings.Purposes.School] > 0)
                {
                    // stop utility
                    alternative.AddUtilityComponent(choiceProbabilityCalculator.GetUtilityComponent(stopComponentIndex));
                }
            }
        }