context: /*
      Copyright 2014-2015 Travel Modelling Group, Department of Civil Engineering, University of Toronto
  
      This file is part of XTMF.
  
      XTMF is free software: you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published by
      the Free Software Foundation, either version 3 of the License, or
      (at your option) any later version.
  
      XTMF is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      GNU General Public License for more details.
  
      You should have received a copy of the GNU General Public License
      along with XTMF.  If not, see <http://www.gnu.org/licenses/>.
  */
  using System;
  using System.Collections.Generic;
  using System.Diagnostics.CodeAnalysis;
  using XTMF;
  using TMG;
  using Tasha.Common;
  using Datastructure;
  using System.Threading.Tasks;
  using TMG.Input;
  using TMG.Functions;
  
  namespace Tasha.StationAccess
  {
  
      [ModuleInformation(Description =
          @"StationAccessChoice is designed to provide the integration for driving to stations in order to take transit.")]
      public class PerceivedTimeStationAccessChoice : IAccessStationChoiceModel
      {
          [RootModule]
          public ITashaRuntime Root;
  
          [RunParameter("Station Zone Ranges", "9000-9999", typeof(RangeSet),
              "A set of ranges that describe which zones represent the stations to have drive access for.")]
          public RangeSet StationZoneRanges;
  
          [RunParameter("Spatial Zones", "1-5999", typeof(RangeSet),
              "The zone numbers for physical zones that we wish to compute.")]
          public RangeSet SpatialZones;
  
          [RunParameter("Mode Name", "DAT", typeof(string), "The name of the mode we should be looking for inside of the trip chain.")]
          public string OurModeName;
  
          private ITashaMode OurMode;
  
          public int[] ClosestStation;
  
          [RunParameter("Minimum Station Utility", 4.53999297624e-5f, "The minimum utility a station is allowed to have.")]
          public float MinimumStationUtility;
  
          [SubModelInformation(Required = true, Description = "Describes the station data.(Origin = Station, Data = capacity)")]
          public IReadODData<float> StationCapacity;
  
          private SparseArray<float> Capacity;
  
          [SubModelInformation(Required = false, Description = "An optional source to gather parking costs from.")]
          public IDataSource<IParkingCost> ParkingModel;
  
          private IParkingCost _parkingModel;
  
          public sealed class TimePeriod : IModule
          {
              [Parameter("StartTime", "6:00AM", typeof(Time), "The start of the time period inclusive")]
              public Time StartTime;
  
              [Parameter("EndTime", "9:00AM", typeof(Time), "The end of the time period exclusive")]
              public Time EndTime;
  
              [RunParameter("Auto Network Name", "Auto", "The name of the auto network to use.")]
              public string AutoNetworkName;
  
              [RunParameter("Transit Network Name", "Transit", "The name of the transit network to use.")]
              public string TransitNetworkName;
  
              [RunParameter("aivtt", 0.0f, "Auto in vehicle time factor.")]
              public float AIVTT;
  
              [RunParameter("acost", 0.0f, "Auto cost factor.")]
              public float AutoCost;
  
              [RunParameter("Perceived Transit Time", 0.0f, "The perceived travel time.")]
              public float PerceivedTransitTime;
  
              [RunParameter("tfare", 0.0f, "Transit fare factor.")]
              public float TransitFare;
  
              [RunParameter("Capacity", 0.0f, "The weight applied to the capacity of the station.")]
              public float Capacity;
  
              [RunParameter("CapacityFactor", 0.0f, "The exponential to apply to the capacity factor for the station.")]
              public float Alpha;
  
              [RunParameter("Parking Cost", 0.0f, "The weight applied to the capacity of the station.")]
              public float ParkingCost;
  
              [RunParameter("Closest Station", 0.0f, "A constant to add for the closest station.")]
              public float ClosestStationFactor;
  
              internal SparseArray<float> CapacityFactor;
  
              [SubModelInformation(Required = true, Description = "The source used to read in the capacity factors")]
              public IDataSource<SparseArray<float>> CapacityFactorSource;
  
              public string Name { get; set; }
  
              public float Progress { get { return 0f; } }
  
              public Tuple<byte, byte, byte> ProgressColour { get { return null; } }
  
              [RootModule]
              public ITravelDemandModel Root;
  
              internal float[] AutoFromOriginToAccessStation;
              internal float[] AutoFromAccessStationToDestination;
  
              internal float[] TransitFromAccessStationToDestination;
              internal float[] TransitFromDestinationToAccessStation;
  
              [RunParameter("Reload Capacity Factors", true, "Set this to false during estimation to help increase performance.")]
              public bool ReloadCapacityFactors;
  
              internal void Load(RangeSet stationRanges, RangeSet spatialZones, SparseArray<float> capacity, int[] closestStation, IParkingCost parkingModel)
              {
                  if (CapacityFactor == null || ReloadCapacityFactors)
                  {
                      LoadCapacityFactors();
                  }
                  CalculateUtilities(stationRanges, spatialZones, capacity.GetFlatData(), closestStation, parkingModel);
              }
  
              [RunParameter("CapacityFactorScale", 1.0f, "A scale on the congestion computed by the capacity factor.")]
              public float CapacityFactorScale;
  
              private void CalculateUtilities(RangeSet stationRanges, RangeSet spatialZones, float[] capacity, int[] closestStation, IParkingCost parkingModel)
              {
                  INetworkData autoNetwork = GetNetwork(AutoNetworkName);
                  ITripComponentData transitNetwork = GetNetwork(TransitNetworkName) as ITripComponentData;
                  EnsureNetworks(autoNetwork, transitNetwork);
                  var zoneArray = Root.ZoneSystem.ZoneArray;
                  IZone[] zones = zoneArray.GetFlatData();
                  int[] stationZones = GetStationZones(stationRanges, capacity, zones);
                  var flatCapacityFactor = CapacityFactor.GetFlatData();
                  if (AutoFromOriginToAccessStation == null || TransitFromAccessStationToDestination.Length != stationZones.Length * zones.Length)
                  {
                      TransitFromAccessStationToDestination = new float[stationZones.Length * zones.Length];
                      AutoFromOriginToAccessStation = new float[stationZones.Length * zones.Length];
                      TransitFromDestinationToAccessStation = new float[stationZones.Length * zones.Length];
                      AutoFromAccessStationToDestination = new float[stationZones.Length * zones.Length];
                  }
  
                  var invStationFactor = new float[stationZones.Length];
                  var parkingUtil = new float[stationZones.Length];
                  for (int i = 0; i < stationZones.Length; i++)
                  {
                      invStationFactor[i] = 1.0f / (((ComputeCapacityFactor(flatCapacityFactor[stationZones[i]], Alpha) - 1.0f) * CapacityFactorScale) + 1.0f);
                      // If we are using the parking model we will need to compute the costs per trip instead of here.
                      parkingUtil[i] = parkingModel == null ? ParkingCost * zones[stationZones[i]].ParkingCost : 0.0f;
                  }
  
                  var fastAuto = autoNetwork as INetworkCompleteData;
                  var fastTransit = transitNetwork as ITripComponentCompleteData;
                  if (fastAuto != null && fastTransit != null)
                  {
                      Parallel.For(0, zones.Length, zoneIndex =>
                      {
                          var autoData = fastAuto.GetTimePeriodData(StartTime);
                          var transitData = fastTransit.GetTimePeriodData(StartTime);
                          if(autoData == null)
                          {
                              throw new XTMFRuntimeException(this, $"In {Name} we were unable to get the auto data from {fastAuto.Name} at the time {StartTime}!");
                          }
                          if (transitData == null)
                          {
                              throw new XTMFRuntimeException(this, $"In {Name} we were unable to get the transit data from {fastTransit.Name} at the time {StartTime}!");
                          }
                          var zoneNumber = zones[zoneIndex].ZoneNumber;
                          if (spatialZones.Contains(zoneNumber))
                          {
                              for (int i = 0; i < stationZones.Length; i++)
                              {
                                  var accessIndex = stationZones[i];
                                  var saveIndex = zoneIndex * stationZones.Length + i;
                                  var accessToZones = accessIndex * zones.Length + zoneIndex;
                                  var zoneToAccess = zoneIndex * zones.Length + accessIndex;
                                  var autoTimeToStation = autoData[zoneToAccess * 2];
                                  if(autoTimeToStation <= 0)
                                  {
                                      throw new XTMFRuntimeException(this, $"There exists an auto access station {zones[stationZones[i]].ZoneNumber}" +
                                          $" that has an capacity however it does not have an auto in vehicle travel time from zone {zoneNumber}!");
                                  }
                                  // calculate access' to access station this will include more factors
                                  AutoFromOriginToAccessStation[saveIndex] = (float)Math.Exp(
                                      AIVTT * autoTimeToStation
  
gt:                                     + AutoCost * autoData[zoneToAccess * 2 + 1]