본문으로 건너뛰기
cccskills
무료GitHub에 공개

4d-simulation

Create 4D construction simulations by linking BIM elements to project schedules. Generate time-based visualizations, sequence analysis, and construction phasing with Gantt integration.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

연동 도구Excel
설치 방법 보기

포함된 파일(3)

  • SKILL.md19.6 KB
  • claw.json552 B
  • instructions.md1.2 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

4D Simulation for Construction

Overview

Based on DDC methodology (Chapter 3.3), this skill implements 4D BIM simulations - linking 3D model elements to the 4th dimension: time. Visualize construction sequences, detect scheduling conflicts, and optimize work phasing.

Book Reference: "4D, 6D-8D и расчет CO2" / "4D-8D BIM and CO2 Calculation"

"4D моделирование позволяет визуализировать последовательность строительства и выявлять конфликты на этапе планирования." — DDC Book, Chapter 3.3

Quick Start

import pandas as pd
from datetime import datetime, timedelta

# BIM elements with schedule data
elements = pd.DataFrame({
    'ElementId': ['E001', 'E002', 'E003', 'E004'],
    'Category': ['Foundation', 'Column', 'Beam', 'Slab'],
    'Level': ['Level 0', 'Level 1', 'Level 1', 'Level 1'],
    'Start_Date': ['2024-01-01', '2024-01-15', '2024-02-01', '2024-02-15'],
    'End_Date': ['2024-01-14', '2024-01-31', '2024-02-14', '2024-02-28'],
    'Phase': ['Structure', 'Structure', 'Structure', 'Structure']
})

elements['Start_Date'] = pd.to_datetime(elements['Start_Date'])
elements['End_Date'] = pd.to_datetime(elements['End_Date'])
elements['Duration_Days'] = (elements['End_Date'] - elements['Start_Date']).dt.days

# Get elements active on a specific date
target_date = pd.to_datetime('2024-01-20')
active_elements = elements[
    (elements['Start_Date'] <= target_date) &
    (elements['End_Date'] >= target_date)
]
print(f"Elements under construction on {target_date.date()}:")
print(active_elements[['ElementId', 'Category']])

4D Data Model

Schedule-Element Linking

import pandas as pd
import numpy as np
from datetime import datetime, timedelta
from typing import List, Dict, Optional

class ScheduleElementLinker:
    """Link BIM elements to schedule activities"""

    def __init__(self, elements_df: pd.DataFrame, schedule_df: pd.DataFrame):
        self.elements = elements_df.copy()
        self.schedule = schedule_df.copy()
        self.links = pd.DataFrame()

    def auto_link_by_category(self, mapping: Dict[str, str]):
        """Auto-link elements to activities by category mapping

        Args:
            mapping: Dict mapping element categories to activity names
                     e.g., {'Wall': 'Structural Walls', 'Slab': 'Floor Construction'}
        """
        links = []

        for category, activity_name in mapping.items():
            # Find elements of this category
            category_elements = self.elements[
                self.elements['Category'] == category
            ]['ElementId'].tolist()

            # Find matching activity
            activity = self.schedule[
                self.schedule['Activity'].str.contains(activity_name, case=False)
            ]

            if not activity.empty and category_elements:
                for elem_id in category_elements:
                    links.append({
                        'ElementId': elem_id,
                        'ActivityId': activity.iloc[0]['ActivityId'],
                        'Activity': activity.iloc[0]['Activity'],
                        'Start_Date': activity.iloc[0]['Start_Date'],
                        'End_Date': activity.iloc[0]['End_Date']
                    })

        self.links = pd.DataFrame(links)
        return self.links

    def auto_link_by_level(self):
        """Auto-link elements based on level and construction sequence"""
        # Get unique levels in order
        levels = sorted(self.elements['Level'].unique())

        links = []
        for i, level in enumerate(levels):
            level_elements = self.elements[self.elements['Level'] == level]

            # Find activity for this level
            level_activity = self.schedule[
                self.schedule['Activity'].str.contains(level, case=False)
            ]

            if not level_activity.empty:
                for _, elem in level_elements.iterrows():
                    links.append({
                        'ElementId': elem['ElementId'],
                        'ActivityId': level_activity.iloc[0]['ActivityId'],
                        'Activity': level_activity.iloc[0]['Activity'],
                        'Start_Date': level_activity.iloc[0]['Start_Date'],
                        'End_Date': level_activity.iloc[0]['End_Date']
                    })

        self.links = pd.DataFrame(links)
        return self.links

    def manual_link(self, element_id: str, activity_id: str):
        """Manually link element to activity"""
        element = self.elements[self.elements['ElementId'] == element_id]
        activity = self.schedule[self.schedule['ActivityId'] == activity_id]

        if element.empty or activity.empty:
            raise ValueError("Element or activity not found")

        new_link = pd.DataFrame([{
            'ElementId': element_id,
            'ActivityId': activity_id,
            'Activity': activity.iloc[0]['Activity'],
            'Start_Date': activity.iloc[0]['Start_Date'],
            'End_Date': activity.iloc[0]['End_Date']
        }])

        self.links = pd.concat([self.links, new_link], ignore_index=True)
        return self.links

    def get_linked_elements(self):
        """Get elements with schedule data"""
        return self.elements.merge(
            self.links[['ElementId', 'ActivityId', 'Start_Date', 'End_Date']],
            on='ElementId',
            how='left'
        )

4D Simulation Engine

class Simulation4D:
    """4D construction simulation engine"""

    def __init__(self, linked_elements: pd.DataFrame):
        self.elements = linked_elements.copy()
        self.elements['Start_Date'] = pd.to_datetime(self.elements['Start_Date'])
        self.elements['End_Date'] = pd.to_datetime(self.elements['End_Date'])

        self.project_start = self.elements['Start_Date'].min()
        self.project_end = self.elements['End_Date'].max()

    def get_state_at_date(self, target_date: datetime) -> pd.DataFrame:
        """Get element states at a specific date"""
        target = pd.to_datetime(target_date)

        # Determine state for each element
        conditions = [
            target < self.elements['Start_Date'],  # Not started
            (self.elements['Start_Date'] <= target) & (target <= self.elements['End_Date']),  # In progress
            target > self.elements['End_Date']  # Completed
        ]
        choices = ['not_started', 'in_progress', 'completed']

        self.elements['State'] = np.select(conditions, choices, default='unknown')

        return self.elements.copy()

    def generate_timeline(self, interval_days: int = 7) -> List[Dict]:
        """Generate timeline snapshots"""
        timeline = []
        current_date = self.project_start

        while current_date <= self.project_end:
            state = self.get_state_at_date(current_date)

            snapshot = {
                'date': current_date,
                'not_started': len(state[state['State'] == 'not_started']),
                'in_progress': len(state[state['State'] == 'in_progress']),
                'completed': len(state[state['State'] == 'completed']),
                'total': len(state)
            }
            snapshot['progress_pct'] = (snapshot['completed'] / snapshot['total']) * 100

            timeline.append(snapshot)
            current_date += timedelta(days=interval_days)

        return timeline

    def get_elements_in_progress(self, target_date: datetime) -> pd.DataFrame:
        """Get elements currently under construction"""
        state = self.get_state_at_date(target_date)
        return state[state['State'] == 'in_progress']

    def analyze_construction_sequence(self) -> pd.DataFrame:
        """Analyze construction sequence by category and level"""
        sequence = self.elements.groupby(['Level', 'Category']).agg({
            'Start_Date': 'min',
            'End_Date': 'max',
            'ElementId': 'count'
        }).rename(columns={'ElementId': 'Element_Count'})

        sequence['Duration_Days'] = (sequence['End_Date'] - sequence['Start_Date']).dt.days
        sequence = sequence.sort_values('Start_Date').reset_index()

        return sequence

    def detect_parallel_work(self) -> pd.DataFrame:
        """Detect work happening in parallel"""
        dates = pd.date_range(self.project_start, self.project_end, freq='D')
        parallel_work = []

        for date in dates:
            state = self.get_state_at_date(date)
            in_progress = state[state['State'] == 'in_progress']

            if len(in_progress) > 1:
                categories = in_progress['Category'].unique().tolist()
                levels = in_progress['Level'].unique().tolist()

                parallel_work.append({
                    'date': date,
                    'parallel_count': len(in_progress),
                    'categories': ', '.join(categories),
                    'levels': ', '.join(levels)
                })

        return pd.DataFrame(parallel_work)

Gantt Chart Integration

Gantt Chart Generator

import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from matplotlib.patches import Patch

class GanttChartGenerator:
    """Generate Gantt charts for 4D simulation"""

    def __init__(self, elements: pd.DataFrame):
        self.elements = elements.copy()
        self.colors = {
            'Foundation': '#8B4513',
            'Column': '#4169E1',
            'Beam': '#228B22',
            'Slab': '#DC143C',
            'Wall': '#FF8C00',
            'Roof': '#9932CC',
            'MEP': '#20B2AA',
            'Finishes': '#FFD700'
        }

    def create_gantt(self, group_by='Category', figsize=(14, 8)):
        """Create Gantt chart grouped by specified column"""
        fig, ax = plt.subplots(figsize=figsize)

        # Group elements
        groups = self.elements.groupby(group_by)

        y_pos = 0
        y_labels = []
        legend_elements = []

        for group_name, group_df in groups:
            color = self.colors.get(group_name, '#808080')

            for _, row in group_df.iterrows():
                start = row['Start_Date']
                duration = (row['End_Date'] - row['Start_Date']).days

                ax.barh(y_pos, duration, left=start, height=0.6,
                       color=color, alpha=0.8, edgecolor='black', linewidth=0.5)

                y_pos += 1

            y_labels.append(group_name)
            legend_elements.append(Patch(facecolor=color, label=group_name))

        # Formatting
        ax.set_yticks(range(len(self.elements)))
        ax.set_yticklabels(self.elements['ElementId'])
        ax.set_xlabel('Date')
        ax.set_title('Construction Schedule - Gantt Chart', fontsize=14, fontweight='bold')

        # Date formatting
        ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
        ax.xaxis.set_major_locator(mdates.WeekdayLocator(interval=2))
        plt.xticks(rotation=45)

        # Legend
        ax.legend(handles=legend_elements, loc='upper right')

        # Grid
        ax.grid(axis='x', alpha=0.3)

        plt.tight_layout()
        return fig

    def create_level_gantt(self, figsize=(14, 10)):
        """Create Gantt chart grouped by level"""
        fig, ax = plt.subplots(figsize=figsize)

        levels = sorted(self.elements['Level'].unique())

        for i, level in enumerate(levels):
            level_elements = self.elements[self.elements['Level'] == level]

            for _, row in level_elements.iterrows():
                color = self.colors.get(row['Category'], '#808080')
                start = row['Start_Date']
                duration = (row['End_Date'] - row['Start_Date']).days

                ax.barh(i, duration, left=start, height=0.4,
                       color=color, alpha=0.8, edgecolor='black', linewidth=0.5)

        ax.set_yticks(range(len(levels)))
        ax.set_yticklabels(levels)
        ax.set_xlabel('Date')
        ax.set_ylabel('Building Level')
        ax.set_title('Construction Sequence by Level', fontsize=14, fontweight='bold')

        ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
        plt.xticks(rotation=45)

        # Add legend
        legend_patches = [Patch(color=c, label=cat) for cat, c in self.colors.items()
                         if cat in self.elements['Category'].values]
        ax.legend(handles=legend_patches, loc='upper right')

        plt.tight_layout()
        return fig

    def create_progress_chart(self, timeline: List[Dict], figsize=(12, 6)):
        """Create S-curve progress chart"""
        df = pd.DataFrame(timeline)

        fig, ax = plt.subplots(figsize=figsize)

        ax.plot(df['date'], df['progress_pct'], 'b-', linewidth=2, label='Progress')
        ax.fill_between(df['date'], 0, df['progress_pct'], alpha=0.3)

        # Add milestones at 25%, 50%, 75%, 100%
        for milestone in [25, 50, 75, 100]:
            ax.axhline(y=milestone, color='gray', linestyle='--', alpha=0.5)
            ax.text(df['date'].iloc[0], milestone + 2, f'{milestone}%',
                   fontsize=9, color='gray')

        ax.set_xlabel('Date')
        ax.set_ylabel('Progress (%)')
        ax.set_title('Project S-Curve', fontsize=14, fontweight='bold')
        ax.set_ylim(0, 105)

        ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
        plt.xticks(rotation=45)

        plt.tight_layout()
        return fig

Sequence Analysis

Construction Sequence Optimizer

class SequenceAnalyzer:
    """Analyze and optimize construction sequences"""

    def __init__(self, elements: pd.DataFrame):
        self.elements = elements.copy()
        self.dependencies = []

    def add_dependency(self, predecessor: str, successor: str, lag_days: int = 0):
        """Add dependency between elements"""
        self.dependencies.append({
            'predecessor': predecessor,
            'successor': successor,
            'lag_days': lag_days
        })

    def check_sequence_violations(self) -> List[Dict]:
        """Check for sequence violations"""
        violations = []

        for dep in self.dependencies:
            pred = self.elements[self.elements['ElementId'] == dep['predecessor']]
            succ = self.elements[self.elements['ElementId'] == dep['successor']]

            if pred.empty or succ.empty:
                continue

            pred_end = pred.iloc[0]['End_Date']
            succ_start = succ.iloc[0]['Start_Date']

            required_start = pred_end + timedelta(days=dep['lag_days'])

            if succ_start < required_start:
                violations.append({
                    'predecessor': dep['predecessor'],
                    'successor': dep['successor'],
                    'predecessor_end': pred_end,
                    'successor_start': succ_start,
                    'required_start': required_start,
                    'violation_days': (required_start - succ_start).days
                })

        return violations

    def check_spatial_conflicts(self) -> List[Dict]:
        """Check for spatial conflicts (same location, same time)"""
        conflicts = []

        # Group by location (Level + Zone if available)
        location_col = 'Level'

        for location, group in self.elements.groupby(location_col):
            if len(group) < 2:
                continue

            # Check for overlapping work
            for i, row1 in group.iterrows():
                for j, row2 in group.iterrows():
                    if i >= j:
                        continue

                    # Check date overlap
                    overlap = (row1['Start_Date'] <= row2['End_Date'] and
                              row2['Start_Date'] <= row1['End_Date'])

                    if overlap:
                        conflicts.append({
                            'location': location,
                            'element1': row1['ElementId'],
                            'element1_category': row1['Category'],
                            'element2': row2['ElementId'],
                            'element2_category': row2['Category'],
                            'overlap_start': max(row1['Start_Date'], row2['Start_Date']),
                            'overlap_end': min(row1['End_Date'], row2['End_Date'])
                        })

        return conflicts

    def calculate_critical_path(self) -> List[str]:
        """Calculate critical path (simplified)"""
        # Build dependency graph
        graph = {}
        for elem in self.elements['ElementId']:
            graph[elem] = {
                'predecessors': [],
                'duration': 0
            }

        for dep in self.dependencies:
            if dep['successor'] in graph:
                graph[dep['successor']]['predecessors'].append(dep['predecessor'])

        # Calculate durations
        for _, row in self.elements.iterrows():
            if row['ElementId'] in graph:
                graph[row['ElementId']]['duration'] = (row['End_Date'] - row['Start_Date']).days

        # Find longest path (simplified critical path)
        def longest_path(node, memo={}):
            if node in memo:
                return memo[node]

            if not graph[node]['predecessors']:
                return graph[node]['duration']

            max_pred = max(
                longest_path(pred, memo) for pred in graph[node]['predecessors']
            )
            memo[node] = max_pred + graph[node]['duration']
            return memo[node]

        # Get all path lengths
        path_lengths = {elem: longest_path(elem) for elem in graph.keys()}

        # Critical path elements
        max_length = max(path_lengths.values())
        critical = [elem for elem, length in path_lengths.items() if length == max_length]

        return critical

Export and Integration

def export_4d_schedule(elements: pd.DataFrame, output_path: str):
    """Export 4D schedule to Excel with multiple views"""
    with pd.ExcelWriter(output_path, engine='openpyxl') as writer:
        # Full schedule
        elements.to_excel(writer, sheet_name='Schedule', index=False)

        # By level
        level_summary = elements.groupby('Level').agg({
            'ElementId': 'count',
            'Start_Date': 'min',
            'End_Date': 'max'
        }).rename(columns={'ElementId': 'Element_Count'})
        level_summary['Duration_Days'] = (level_summary['End_Date'] - level_summary['Start_Date']).dt.days
        level_summary.to_excel(writer, sheet_name='By_Level')

        # By category
        cat_summary = elements.groupby('Category').agg({
            'ElementId': 'count',
            'Start_Date': 'min',
            'End_Date': 'max'
        }).rename(columns={'ElementId': 'Element_Count'})
        cat_summary.to_excel(writer, sheet_name='By_Category')

    return output_path

Quick Reference

ConceptDescription
4D = 3D + TimeBIM model linked to schedule
ActivityScheduled work item
Element Statenot_started / in_progress / completed
Critical PathLongest sequence determining project duration

Resources

Next Steps

  • See gantt-chart for schedule visualization
  • See co2-estimation for 6D (sustainability) analysis
  • See clash-detection-analysis for 4D conflict detection

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

Analyze 5000+ IFC and Revit projects at scale for patterns, benchmarks, and insights. Big data analysis for construction.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

datadrivenconstruction/DDC_Skills_for_AI_Agents_in_Construction3592026년 10월 20일 업데이트

Check construction AI systems against the EU AI Act (Regulation 2024/1689): classify risk of estimation, scheduling, CV and agent tools, document transparency, keep human oversight. Use when deploying or auditing AI in construction.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

datadrivenconstruction/DDC_Skills_for_AI_Agents_in_Construction3592026년 10월 20일 업데이트

Orchestrate multiple AI agents for construction workflows: estimator, scheduler, document, QA and safety agents coordinated by a supervisor agent, with human checkpoints. Use when automating end-to-end project processes with agentic AI.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

datadrivenconstruction/DDC_Skills_for_AI_Agents_in_Construction3592026년 10월 20일 업데이트

Create Apache Airflow DAGs for construction data pipelines. Orchestrate ETL, validation, and reporting workflows.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

datadrivenconstruction/DDC_Skills_for_AI_Agents_in_Construction3592026년 10월 20일 업데이트

Automate as-built documentation and digital handover for construction. Compile project records, generate O&M manuals, create asset databases, and ensure complete project closeout.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

datadrivenconstruction/DDC_Skills_for_AI_Agents_in_Construction3592026년 10월 20일 업데이트

Manage as-built documentation for project closeout. Track drawing markups, coordinate updates, and verify completeness.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

datadrivenconstruction/DDC_Skills_for_AI_Agents_in_Construction3592026년 10월 20일 업데이트

datadrivenconstruction의 스킬 모두 보기

이 스킬의 문제 신고