1313# Built-Ins
1414import os
1515import pathlib
16+ import itertools
1617import functools
1718
1819from typing import Any
3738from normits_demand .matrices import pa_to_od
3839from normits_demand .matrices import utils as mat_utils
3940from normits_demand .reports import matrix_reports
41+ from normits_demand .concurrency import multiprocessing
4042
4143from normits_demand .pathing .distribution_model import DistributionModelExportPaths
4244from normits_demand .pathing .distribution_model import DMArgumentBuilderBase
@@ -742,7 +744,87 @@ def run_od_matrix_reports(self):
742744 col_name = 'destinations' ,
743745 )
744746
745- def compile_to_assignment_format (self ):
747+ def _convert_matrix_time_format (
748+ self ,
749+ import_dir : pathlib .Path ,
750+ export_dir : pathlib .Path ,
751+ from_time_format : nd .core .TimeFormat = None ,
752+ to_time_format : nd .core .TimeFormat = None ,
753+ ) -> None :
754+ """Converts matrices between time formats"""
755+ # TODO(BT): This function just assumes there's time periods.
756+ # Won't work otherwise
757+ conversion_factors = from_time_format .get_conversion_factors (to_time_format )
758+
759+ # Build matrix naming templates
760+ template = self .running_segmentation .generate_template_file_name (
761+ file_desc = '{matrix_format}' ,
762+ trip_origin = self .trip_origin ,
763+ year = str (self .year ),
764+ compressed = True ,
765+ )
766+
767+ if self .trip_origin == nd .core .TripOrigin .HB .value :
768+ matrix_formats = ["synthetic_od_from" , "synthetic_od_to" ]
769+ elif self .trip_origin == nd .core .TripOrigin .NHB .value :
770+ matrix_formats = ["synthetic_od" ]
771+ else :
772+ raise ValueError (f"Trip origin '{ self .trip_origin } ' not recognised." )
773+
774+ # Build the multiprocessing kwargs
775+ kwarg_list = list ()
776+
777+ # BACKLOG(BT): This is all a really rough kludge to get this working
778+ # NOW. Need to come back and think how to do this properly.
779+ if self .running_segmentation .has_time_period_segments ():
780+ iterator = itertools .product (self .running_segmentation , [- 1 ])
781+ naming_order = self .running_segmentation .naming_order
782+ segment_types = self .running_segmentation .segment_types
783+ else :
784+ tps = [1 , 2 , 3 , 4 , 5 , 6 ]
785+ iterator = itertools .product (self .running_segmentation , tps )
786+ naming_order = self .running_segmentation .naming_order + ['tp' ]
787+ segment_types = self .running_segmentation .segment_types | {"tp" : int }
788+
789+ for segment_params , tp in iterator :
790+ # Generate filenames
791+ tp_params = segment_params .copy ()
792+ if "tp" not in tp_params :
793+ tp_params ['tp' ] = tp
794+ segment_str = nd .core .SegmentationLevel .generate_template_segment_str (
795+ naming_order = naming_order ,
796+ segment_params = tp_params ,
797+ segment_types = segment_types ,
798+ )
799+
800+ # Build the kwarg list
801+ for mx_format in matrix_formats :
802+ fname = template .format (segment_params = segment_str , matrix_format = mx_format )
803+ kwarg_list .append ({
804+ "input_path" : import_dir / fname ,
805+ "output_path" : export_dir / fname ,
806+ "factor" : conversion_factors [tp_params ["tp" ]]
807+ })
808+
809+ # MP running
810+ self ._logger .info (
811+ f"Converting OD matrix time format from { from_time_format .value } "
812+ f"to { to_time_format .value } ."
813+ )
814+ pbar_kwargs = {'desc' : "Converting OD matrix time format" }
815+ multiprocessing .multiprocess (
816+ fn = mat_utils .apply_factor ,
817+ kwargs = kwarg_list ,
818+ process_count = self .process_count ,
819+ pbar_kwargs = pbar_kwargs
820+
821+ )
822+
823+ def compile_to_assignment_format (
824+ self ,
825+ from_time_format : nd .core .TimeFormat = None ,
826+ to_time_format : nd .core .TimeFormat = None ,
827+ ):
746828 """TfN Specific helper function to compile outputs into assignment format
747829
748830 This should really be the job of NorMITs Matrix tools! Move there
@@ -752,18 +834,32 @@ def compile_to_assignment_format(self):
752834 -------
753835
754836 """
755- # TODO(BT): NEED TO OUTPUT SPLITTING FACTORS
756-
757837 # TODO(BT): UPDATE build_compile_params() to use segmentation levels
758838 m_needed = self .running_segmentation .segments ['m' ].unique ()
759839
760840 # NoHAM should be tp split
761841 tp_needed = [1 , 2 , 3 , 4 ]
762842
843+ # Covert time periods if factors given
844+ od_mat_dir = self .export_paths .full_od_dir
845+ if (
846+ (from_time_format is not None and to_time_format is not None )
847+ and (from_time_format != to_time_format )
848+ ):
849+ new_od_mat_dir = pathlib .Path (self .export_paths .full_od_dir ) / "converted time format"
850+ new_od_mat_dir .mkdir (exist_ok = True )
851+ self ._convert_matrix_time_format (
852+ import_dir = pathlib .Path (self .export_paths .full_od_dir ),
853+ export_dir = pathlib .Path (new_od_mat_dir ),
854+ from_time_format = from_time_format ,
855+ to_time_format = to_time_format ,
856+ )
857+ od_mat_dir = new_od_mat_dir
858+
763859 if self .running_mode in [nd .Mode .CAR , nd .Mode .BUS ]:
764860 # Compile to NoHAM format
765861 compile_params_paths = matrix_processing .build_compile_params (
766- import_dir = self . export_paths . full_od_dir ,
862+ import_dir = od_mat_dir ,
767863 export_dir = self .export_paths .compiled_od_dir ,
768864 matrix_format = self ._od_matrix_desc ,
769865 years_needed = [self .year ],
@@ -772,9 +868,10 @@ def compile_to_assignment_format(self):
772868 )
773869
774870 matrix_processing .compile_matrices (
775- mat_import = self . export_paths . full_od_dir ,
871+ mat_import = od_mat_dir ,
776872 mat_export = self .export_paths .compiled_od_dir ,
777873 compile_params_path = compile_params_paths [0 ],
874+ factors_fname = "od_compilation_factors.pkl" ,
778875 )
779876
780877 # TODO(BT): Build in DM imports!
@@ -815,7 +912,7 @@ def compile_to_assignment_format(self):
815912 self ._logger .info ("Compiling NoRMS VDM Format" )
816913 matrix_processing .compile_norms_to_vdm (
817914 mat_pa_import = self .export_paths .full_tp_pa_dir ,
818- mat_od_import = self . export_paths . full_od_dir ,
915+ mat_od_import = od_mat_dir ,
819916 mat_export = self .export_paths .compiled_pa_dir , # TODO(BT): Rename to NoRMS
820917 params_export = self .export_paths .compiled_pa_dir ,
821918 year = self .year ,
0 commit comments