6363 "VOLUME_MESH" : "VolumeMesher" ,
6464}
6565
66+ TERMINAL_ERRORS = {
67+ "validate_fail" ,
68+ "error" ,
69+ "diverged" ,
70+ "blocked" ,
71+ "aborting" ,
72+ "aborted" ,
73+ }
74+
75+ RUN_STATUSES = [
76+ "draft" ,
77+ "preprocess" ,
78+ "validating" ,
79+ "validate" ,
80+ "running" ,
81+ "postprocess" ,
82+ "success" ,
83+ ]
84+
6685
6786def _get_url (task_id : str ) -> str :
6887 """Get the URL for a task on our server."""
@@ -1070,21 +1089,13 @@ def _status_to_stage(status: str) -> tuple[str, int]:
10701089
10711090 # Non-verbose path: poll without progress bars then return
10721091 if not verbose :
1073- terminal_errors = {
1074- "validate_fail" ,
1075- "error" ,
1076- "diverged" ,
1077- "blocked" ,
1078- "aborting" ,
1079- "aborted" ,
1080- }
10811092 # Run phase
10821093 while True :
10831094 d = _batch_detail (batch_id )
10841095 s = d .totalStatus .value
10851096 total = d .totalTask or 0
10861097 r = d .runSuccess or 0
1087- if s in terminal_errors :
1098+ if s in TERMINAL_ERRORS :
10881099 raise WebError (f"Batch { batch_id } terminated: { s } " )
10891100 if total and r >= total :
10901101 break
@@ -1096,7 +1107,7 @@ def _status_to_stage(status: str) -> tuple[str, int]:
10961107 postprocess_status = d .postprocessStatus
10971108 if postprocess_status == "success" :
10981109 break
1099- elif postprocess_status in terminal_errors :
1110+ elif postprocess_status in TERMINAL_ERRORS :
11001111 raise WebError (
11011112 f"Batch { batch_id } terminated. Please contact customer support and provide this Component Modeler batch ID: '{ batch_id } '"
11021113 )
@@ -1110,20 +1121,9 @@ def _status_to_stage(status: str) -> tuple[str, int]:
11101121 TimeElapsedColumn (),
11111122 )
11121123 with Progress (* progress_columns , console = console , transient = False ) as progress :
1113- terminal_errors = {"validate_fail" , "error" , "diverged" , "blocked" , "aborting" , "aborted" }
1114-
11151124 # Phase: Run (aggregate + per-task)
11161125 p_run = progress .add_task ("Run Total" , total = 1.0 )
11171126 task_bars : dict [str , int ] = {}
1118- run_statuses = [
1119- "draft" ,
1120- "preprocess" ,
1121- "validating" ,
1122- "validate" ,
1123- "running" ,
1124- "postprocess" ,
1125- "success" ,
1126- ]
11271127
11281128 while True :
11291129 detail = _batch_detail (batch_id )
@@ -1140,8 +1140,8 @@ def _status_to_stage(status: str) -> tuple[str, int]:
11401140 _ , idx = _status_to_stage (tstatus )
11411141 pbar = progress .add_task (
11421142 f" { name } " ,
1143- total = len (run_statuses ) - 1 ,
1144- completed = min (idx , len (run_statuses ) - 1 ),
1143+ total = len (RUN_STATUSES ) - 1 ,
1144+ completed = min (idx , len (RUN_STATUSES ) - 1 ),
11451145 )
11461146 task_bars [name ] = pbar
11471147
@@ -1174,7 +1174,7 @@ def _status_to_stage(status: str) -> tuple[str, int]:
11741174
11751175 if total and r >= total :
11761176 break
1177- if status in terminal_errors :
1177+ if status in TERMINAL_ERRORS :
11781178 raise WebError (f"Batch { batch_id } terminated: { status } " )
11791179 progress .refresh ()
11801180 time .sleep (REFRESH_TIME )
@@ -1195,7 +1195,7 @@ def _status_to_stage(status: str) -> tuple[str, int]:
11951195 progress .update (p_post , completed = 0.33 )
11961196 elif postprocess_status == "running" :
11971197 progress .update (p_post , completed = 0.55 )
1198- elif postprocess_status in terminal_errors :
1198+ elif postprocess_status in TERMINAL_ERRORS :
11991199 raise WebError (
12001200 f"Batch { batch_id } terminated. Please contact customer support and provide this Component Modeler batch ID: '{ batch_id } '"
12011201 )
@@ -1355,22 +1355,40 @@ def estimate_cost(
13551355 console = get_logging_console () if verbose else None
13561356
13571357 if _is_modeler_batch (task_id ):
1358- status = _batch_detail (task_id ).totalStatus .value
1358+ d = _batch_detail (task_id )
1359+ status = d .totalStatus .value
13591360
13601361 # Wait for a termination status
13611362 while status not in ["validate_success" , "success" , "error" , "failed" ]:
13621363 time .sleep (REFRESH_TIME )
1363- status = _batch_detail (task_id ).totalStatus .value
1364+ d = _batch_detail (task_id )
1365+ status = d .totalStatus .value
13641366
1365- if status in ["validate_success" , "success" ]:
1366- est_flex_unit = _batch_detail (task_id ).estFlexUnit
1367- if verbose :
1368- console .log (
1369- f"Maximum FlexCredit cost: { est_flex_unit :1.3f} . Minimum cost depends on "
1370- "task execution details. Use 'web.real_cost(task_id)' to get the billed FlexCredit "
1371- "cost after a simulation run."
1372- )
1373- return est_flex_unit
1367+ if status in ["validate_success" , "success" ]:
1368+ est_flex_unit = _batch_detail (task_id ).estFlexUnit
1369+ if verbose :
1370+ console .log (
1371+ f"Maximum FlexCredit cost: { est_flex_unit :1.3f} . Minimum cost depends on "
1372+ "task execution details. Use 'web.real_cost(task_id)' to get the billed FlexCredit "
1373+ "cost after a simulation run."
1374+ )
1375+ return est_flex_unit
1376+ elif status in TERMINAL_ERRORS :
1377+ log .error (f"The ComponentModeler '{ task_id } ' has failed: { status } " )
1378+
1379+ if status == "validate_fail" :
1380+ assert d .validateErrors is not None
1381+ for key , error in d .validateErrors .items ():
1382+ # I don't like this ideally but would like to control the endpoint to make this better
1383+ error_dict = json .loads (error )
1384+ validation_error = error_dict ["validation_error" ]
1385+ log .error (
1386+ f"Subtask '{ key } ' has failed to validate:"
1387+ f" \n { validation_error } \n "
1388+ f"Fix your component modeler configuration. "
1389+ f"Generate subtask simulations locally using `ComponentModelerType.sim_dict`."
1390+ )
1391+ break
13741392 else :
13751393 task = SimulationTask .get (task_id )
13761394
0 commit comments