fib-international / structuralcodes

A Python library for structural engineering calculations
https://fib-international.github.io/structuralcodes/
Apache License 2.0
79 stars 22 forks source link

[enhancement] Create Abstract Class for Codes #198

Open mtabbasi opened 6 days ago

mtabbasi commented 6 days ago

Hello,

I would like to propose a structural improvement to this repository that could enhance code organization, flexibility, and maintainability.

Suggestion:

Example:

Define a base class that enforces common methods and properties that every design code must implement:

from abc import ABC, abstractmethod

class StructuralCode(ABC):
    @abstractmethod
    def compressive_strength(self):
        pass

    @abstractmethod
    def tensile_strength(self):
        pass

    @abstractmethod
    def modulus_of_elasticity(self):
        pass

    @abstractmethod
    def strain_at_peak_stress(self):
        pass

    @abstractmethod
    def time_dependent_properties(self):
        pass

Now, specific design codes can inherit from this abstract class and implement their own logic. For instance, a class based on Eurocode 2 (2004) for concrete material properties might look like this:

import math

class Eurocode2004Concrete(StructuralCode):
    def __init__(self, f_ck, aggregate_type="normal", cement_class="normal", age_days=28):
        self.f_ck = f_ck
        self.aggregate_type = aggregate_type
        self.cement_class = cement_class
        self.age_days = age_days
        self.f_cm = f_ck + 8  # Mean compressive strength as per Eurocode 2

    def compressive_strength(self):
        if self.age_days == 28:
            return self.f_ck
        else:
            beta = math.exp(0.2 * (1 - (28 / self.age_days) ** (1/2)))
            return self.f_ck * beta

    def tensile_strength(self):
        return 0.3 * self.f_ck ** (2/3)

    def modulus_of_elasticity(self):
        return 22 * (self.f_cm / 10) ** 0.3

    def strain_at_peak_stress(self):
        return 0.002 + 0.00085 * (self.f_ck - 50) / 40 if self.f_ck > 50 else 0.002

    def time_dependent_properties(self):
        return {
            "creep_coefficient": 2.5,
            "shrinkage_strain": 0.00035
        }

Benefits:

mortenengen commented 5 days ago

Thanks for posting the issue, @mtabbasi. If I understand correctly, you are describing a design that is quite similar to our Material classes. We are working on describing this and more in our docs, so please accept my apology that this was not clear from the code.

Please let me describe how we have designed the hierarchy. You can find a brief description of the hierarchy in our docs.

  1. On the lowest level, we implement functions that represent equations from the design codes. We aim at having function names that as close as possible match the names of the quantities that are calculated in the design codes. We collect the functions in separate sub-modules of structuralcodes and expose all necessary unctions in the public API related to each code. One effect of this is, as you are highlighting, that one quantity can have slightly different names in different codes.
  2. One the second level we have classes that represent materials and constitutive laws from design codes. The material classes are all based on a hierarchy of base classes described in our docs. Since we are based on common base classes, we define a common interface although, e.g. the Concrete EC2_2004 and the ConcreteMC2010 are based on two different codes with slightly different names.
  3. On the third level, we define geometries and sections. The sections use the interface defined in the materials to calculate section responses.

Back to your suggestion: if I receive your question correctly, I think that we already implemented the material classes similar to what you are suggesting, and we are seeing the benefits that you describe in the end 😃

Please get back to me if you wish to elaborate further on the topic!