From 2204bac92b97d377adedebb80e9d801597057d05 Mon Sep 17 00:00:00 2001 From: Cizz22 Date: Mon, 13 Oct 2025 19:08:55 +0700 Subject: [PATCH] add statistic --- src/calculation_target_reliability/utils.py | 4 +- src/calculation_time_constrains/model.py | 4 + src/calculation_time_constrains/router.py | 4 +- src/calculation_time_constrains/service.py | 101 ++++++++++++-------- 4 files changed, 68 insertions(+), 45 deletions(-) diff --git a/src/calculation_target_reliability/utils.py b/src/calculation_target_reliability/utils.py index 6dd1b69..59ed5b7 100644 --- a/src/calculation_target_reliability/utils.py +++ b/src/calculation_target_reliability/utils.py @@ -73,7 +73,7 @@ async def wait_for_workflow(simulation_id, max_retries=3): print(f"✅ Workflow {workflow_id} finished with status: {status}") break - print(f"⏳ Workflow {workflow_id} still {status}, checking again in 30s...") + print(f"⏳ Workflow {workflow_id} still {status}, checking again in 10s...") except Exception as e: retries += 1 @@ -86,6 +86,6 @@ async def wait_for_workflow(simulation_id, max_retries=3): continue retries = 0 # reset retries if describe() worked - await asyncio.sleep(30) + await asyncio.sleep(10) return simulation_id \ No newline at end of file diff --git a/src/calculation_time_constrains/model.py b/src/calculation_time_constrains/model.py index 5380f9d..8f82da9 100644 --- a/src/calculation_time_constrains/model.py +++ b/src/calculation_time_constrains/model.py @@ -71,6 +71,8 @@ class CalculationData(Base, DefaultMixin, IdentityMixin): max_interval = Column(Integer, nullable=True) rbd_simulation_id = Column(UUID(as_uuid=True), nullable=True) + + optimum_analysis = Column(JSON, nullable=True) session = relationship("OverhaulScope", lazy="raise") @@ -80,7 +82,9 @@ class CalculationData(Base, DefaultMixin, IdentityMixin): "CalculationEquipmentResult", lazy="raise", viewonly=True ) + results = relationship("CalculationResult", lazy="raise", viewonly=True) + @classmethod async def create_with_param( diff --git a/src/calculation_time_constrains/router.py b/src/calculation_time_constrains/router.py index 02ac112..e2af527 100644 --- a/src/calculation_time_constrains/router.py +++ b/src/calculation_time_constrains/router.py @@ -25,7 +25,7 @@ router = APIRouter() @router.post( - "", response_model=StandardResponse[Union[str, CalculationTimeConstrainsRead]] + "", response_model=StandardResponse[Union[dict, CalculationTimeConstrainsRead]] ) async def create_calculation_time_constrains( token: Token, @@ -55,7 +55,7 @@ async def create_calculation_time_constrains( simulation_id=simulation_id ) - return StandardResponse(data=str(results), message="Data created successfully") + return StandardResponse(data=results, message="Data created successfully") @router.get( diff --git a/src/calculation_time_constrains/service.py b/src/calculation_time_constrains/service.py index 6c0a6ed..8ab722b 100644 --- a/src/calculation_time_constrains/service.py +++ b/src/calculation_time_constrains/service.py @@ -334,7 +334,7 @@ class OptimumCostModelWithSpareparts: async def calculate_cost_all_equipment_with_spareparts(self, db_session,collector_db_session ,equipments: List, calculation, preventive_cost: float, - simulation_id: str = "default") -> Dict: + simulation_id: str = "default"): """ Calculate optimal overhaul timing for entire fleet considering sparepart constraints """ @@ -373,7 +373,13 @@ class OptimumCostModelWithSpareparts: plant_capacity_loss_money = [metrics['derated_mwh'] * COST_PER_MWH for metrics in plant_monthly_metrics.values()] cumulative_loss_money = np.cumsum(plant_capacity_loss_money) - + + total_simulation_period = int((importance_results["plant_result"]['total_downtime'] + importance_results["plant_result"]['total_uptime'])/720) + + loss_production_per_month = np.arange(0, total_simulation_period) + k = 5 + + loss_exp = (importance_results["plant_result"]['total_downtime'] * 660 * 500_000) * (np.exp(k * (loss_production_per_month / total_simulation_period)) - 1) / (np.exp(k) - 1) for equipment in equipments: @@ -434,7 +440,7 @@ class OptimumCostModelWithSpareparts: # Phase 3: Generate final results and database objects fleet_results = [] - total_corrective_costs = np.zeros(max_interval) + cumulative_loss_money[0:max_interval] + total_corrective_costs = np.zeros(max_interval) + loss_exp[0:max_interval] total_preventive_costs = np.zeros(max_interval) total_procurement_costs = np.zeros(max_interval) total_costs = np.zeros(max_interval) @@ -507,7 +513,7 @@ class OptimumCostModelWithSpareparts: f"(Procurement cost: ${cost_data['procurement_cost']:,.2f})") # Calculate fleet optimal interval - fleet_optimal_index = np.argmin(total_costs) + fleet_optimal_index = np.argmin(total_corrective_costs + total_preventive_costs) fleet_optimal_cost = total_costs[fleet_optimal_index] # Generate procurement optimization report @@ -528,26 +534,7 @@ class OptimumCostModelWithSpareparts: self.logger.info(f" - Total procurement cost: ${total_fleet_procurement_cost:,.2f}") self.logger.info(f" - Equipment with sparepart constraints: {len([r for r in individual_results.values() if not r['sparepart_available']])}") - return { - 'id': calculation.id, - 'fleet_results': fleet_results, - 'fleet_optimal_interval': fleet_optimal_index + 1, - 'fleet_optimal_cost': fleet_optimal_cost, - 'total_corrective_costs': total_corrective_costs.tolist(), - 'total_preventive_costs': total_preventive_costs.tolist(), - 'total_procurement_costs': total_procurement_costs.tolist(), - 'individual_results': individual_results, - 'optimized_plan': improved_plan, - 'procurement_plan': procurement_plan, - 'total_fleet_procurement_cost': total_fleet_procurement_cost, - 'analysis_parameters': { - 'planned_oh_months': self.planned_oh_months, - 'analysis_window_months': self.time_window_months, - 'last_oh_date': self.last_oh_date.isoformat(), - 'next_oh_date': self.next_oh_date.isoformat(), - 'sparepart_optimization_enabled': True - } - } + return fleet_optimal_index def _optimize_fleet_with_sparepart_constraints(self, individual_results: Dict, equipments: List, equipment_birnbaum: Dict, simulation_id: str) -> List[Tuple[str, int]]: @@ -713,27 +700,58 @@ async def run_simulation_with_spareparts(*, db_session, calculation, token: str, base_url=RBD_SERVICE_API, sparepart_manager=sparepart_manager ) + + simulation_ids = ["41a9b376-fe55-4cec-9234-db4951019b8a", + "ea3c8d32-fb1e-417c-914a-296ba08516a1", + "ed586b3d-c215-4b58-a6a8-7577f91e0b6b", + "97357934-8703-4ef5-90c8-858d3a5c5a35", + "f674918f-af74-4601-99a3-96f44bfdb4e4", + "a2a7fc3e-f638-4c88-830c-88232b45d2f5", + "18012150-9458-419a-9537-de8f096f2e36", + "835d5027-6abf-47d8-af04-f3428634d118", + "37c68489-23e2-4def-ae31-81dc79df7851", + "ce7a1d4f-c9c4-41c1-97ee-20911d354bba"] try: - # Run fleet optimization with sparepart management - results = await optimum_oh_model.calculate_cost_all_equipment_with_spareparts( - db_session=db_session, - collector_db_session=collector_db_session, - equipments=equipments, - calculation=calculation_data, - preventive_cost=calculation_data.parameter.overhaul_cost, - simulation_id=simulation_id - ) - - # Generate sparepart report - # sparepart_report = optimum_oh_model.generate_sparepart_report(results) - # print(sparepart_report) - - return results - + optimum_collection = [] + + for sim_id in simulation_ids: + # Run fleet optimization with sparepart management + results = await optimum_oh_model.calculate_cost_all_equipment_with_spareparts( + db_session=db_session, + collector_db_session=collector_db_session, + equipments=equipments, + calculation=calculation_data, + preventive_cost=calculation_data.parameter.overhaul_cost, + simulation_id=sim_id + ) + + optimum_collection.append(results) finally: await optimum_oh_model._close_session() + + np_optimum = np.array(optimum_collection) + + stats = { + 'min': float(np.min(np_optimum)), + 'max': float(np.max(np_optimum)), + 'mean': float(np.mean(np_optimum)), + 'median': float(np.median(np_optimum)), + 'std': float(np.std(np_optimum)), + 'variance': float(np.var(np_optimum)), + 'range': float(np.ptp(np_optimum)), # max - min + } + + calculation_data.optimum_analysis = stats + + await db_session.commit() + + return { + "id": calculation_data.id, + "optimum": stats + } + async def create_param_and_data( @@ -992,7 +1010,8 @@ async def get_calculation_result(db_session: DbSession, calculation_id: str, tok "next_planned_overhaul": scope_overhaul.start_date.isoformat(), "calculation_type": "sparepart_optimized" if fleet_statistics['equipment_with_sparepart_constraints'] > 0 else "standard", "total_equipment_analyzed": len(all_equipment), - "included_in_optimization": len(included_equipment) + "included_in_optimization": len(included_equipment), + "optimal_stat": scope_calculation.optimum_analysis } )