Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
58 changes: 58 additions & 0 deletions .cursor/rules/mod-000a-reusable-layers-belong-in-nn.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
---
description: Reusable layers and building blocks should be placed in physicsnemo/nn, not physicsnemo/models. Examples include FullyConnected, attention layers, and UNetBlock.
alwaysApply: false
---

When creating or refactoring reusable layer code, rule MOD-000a must be followed. Explicitly reference "Following rule MOD-000a, which states that reusable layers should go in physicsnemo/nn..." when explaining placement decisions.

## MOD-000a: Reusable layers/blocks belong in physicsnemo.nn

**Description:**

Reusable layers that are the building blocks of more complex architectures
should go into `physicsnemo/nn`. Those include for instance `FullyConnected`,
various variants of attention layers, `UNetBlock` (a block of a U-Net), etc.

All layers that are directly exposed to the user should be imported in
`physicsnemo/nn/__init__.py`, such that they can be used as follows:

```python
from physicsnemo.nn import MyLayer
```

The only exception to this rule is for layers that are highly specific to a
single example. In this case, it may be acceptable to place them in a module
specific to the example code, such as `examples/<example_name>/utils/nn.py`.

**Rationale:**

Ensures consistency in the organization of reusable layers in the repository.
Keeping all reusable components in a single location makes them easy to find
and promotes code reuse across different models.

**Example:**

```python
# Good: Reusable layer in physicsnemo/nn/attention.py
class MultiHeadAttention(Module):
"""A reusable attention layer that can be used in various architectures."""
pass

# Good: Import in physicsnemo/nn/__init__.py
from physicsnemo.nn.attention import MultiHeadAttention

# Good: Example-specific layer in examples/weather/utils/nn.py
class WeatherSpecificLayer(Module):
"""Layer highly specific to the weather forecasting example."""
pass
```

**Anti-pattern:**

```python
# WRONG: Reusable layer placed in physicsnemo/models/
# File: physicsnemo/models/attention.py
class MultiHeadAttention(Module):
"""Should be in physicsnemo/nn/ not physicsnemo/models/"""
pass
```
54 changes: 54 additions & 0 deletions .cursor/rules/mod-000b-complete-models-belong-in-models.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
---
description: Complete models composed of multiple layers should be placed in physicsnemo/models, not physicsnemo/nn. These are domain-specific or modality-specific models.
alwaysApply: false
---

When creating or refactoring complete model code, rule MOD-000b must be followed. Explicitly reference "Following rule MOD-000b, which states that complete models should go in physicsnemo/models..." when explaining placement decisions.

## MOD-000b: Complete models belong in physicsnemo.models

**Description:**

More complete models, composed of multiple layers and/or other sub-models,
should go into `physicsnemo/models`. All models that are directly exposed to
the user should be imported in `physicsnemo/models/__init__.py`, such that they
can be used as follows:

```python
from physicsnemo.models import MyModel
```

The only exception to this rule is for models that are highly specific to a
single example. In this case, it may be acceptable to place them in a module
specific to the example code, such as `examples/<example_name>/utils/nn.py`.

**Rationale:**

Ensures consistency and clarity in the organization of models in the repository,
in particular a clear separation between reusable layers and more complete
models that are applicable to a specific domain or specific data modality.

**Example:**

```python
# Good: Complete model in physicsnemo/models/transformer.py
class TransformerModel(Module):
"""A complete transformer model composed of attention and feedforward layers."""
def __init__(self):
super().__init__()
self.attention = MultiHeadAttention(...)
self.ffn = FeedForward(...)

# Good: Import in physicsnemo/models/__init__.py
from physicsnemo.models.transformer import TransformerModel
```

**Anti-pattern:**

```python
# WRONG: Complete model placed in physicsnemo/nn/
# File: physicsnemo/nn/transformer.py
class TransformerModel(Module):
"""Should be in physicsnemo/models/ not physicsnemo/nn/"""
pass
```
47 changes: 47 additions & 0 deletions .cursor/rules/mod-001-use-physicsnemo-module-as-base-class.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
---
description: All model and layer classes must inherit from physicsnemo.Module (not torch.nn.Module directly) to ensure proper serialization, versioning, and registry functionality.
alwaysApply: false
---

When creating or modifying model classes, rule MOD-001 must be strictly followed. Explicitly reference "Following rule MOD-001, which states that all model classes must inherit from physicsnemo.Module..." when explaining inheritance decisions.

## MOD-001: Use physicsnemo.Module as model base classes

**Description:**

All model classes must inherit from `physicsnemo.Module`. Direct subclasses of
`torch.nn.Module` are not allowed. Direct subclasses of `physicsnemo.Module`
are allowed (note that `physicsnemo.Module` is a subclass of `torch.nn.Module`).
Ensure proper initialization of parent classes using `super().__init__()`. Pass
the `meta` argument to the `super().__init__()` call if appropriate, otherwise
set it manually with `self.meta = meta`.

**Rationale:**

Ensures invariants and functionality of the `physicsnemo.Module` class for all
models. In particular, instances of `physicsnemo.Module` benefit from features
that are not available in `torch.nn.Module` instances. Those include serialization
for checkpointing and loading modules and submodules, versioning system to
handle backward compatibility, as well as ability to be registered in the
`physicsnemo.registry` for easy instantiation and use in any codebase.

**Example:**

```python
from physicsnemo import Module

class MyModel(Module):
def __init__(self, input_dim: int, output_dim: int):
super().__init__(meta=MyModelMetaData())
self.linear = nn.Linear(input_dim, output_dim)
```

**Anti-pattern:**

```python
from torch import nn

class MyModel(nn.Module):
def __init__(self, input_dim: int, output_dim: int):
self.linear = nn.Linear(input_dim, output_dim)
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
---
description: New model classes should start in physicsnemo/experimental/nn or physicsnemo/experimental/models during development, where backward compatibility is not guaranteed.
alwaysApply: false
---

When creating new model or layer classes, rule MOD-002a must be followed. Explicitly reference "Following rule MOD-002a, which states that new models should start in physicsnemo/experimental/..." when explaining where to place new code.

## MOD-002a: New models and layers belong in physicsnemo.experimental

**Description:**

For the vast majority of models, new classes are created either in
`physicsnemo/experimental/nn` for reusable layers, or in
`physicsnemo/experimental/models` for more complete models. The `experimental`
folder is used to store models that are still under development (beta or alpha
releases) during this stage, backward compatibility is not guaranteed.

One exception is when the developer is highly confident that the model is
sufficiently mature and applicable to many domains or use cases. In this case
the model class can be created in the `physicsnemo/nn` or `physicsnemo/models`
folders directly, and backward compatibility is guaranteed.

Another exception is when the model class is highly specific to a single
example. In this case, it may be acceptable to place it in a module specific to
the example code, such as `examples/<example_name>/utils/nn.py`.

After staying in experimental for a sufficient amount of time (typically at
least 1 release cycle), the model class can be promoted to production. It is
then moved to the `physicsnemo/nn` or `physicsnemo/models` folders, based on
whether it's a reusable layer or complete model (see MOD-000a and MOD-000b).

**Note:** Per MOD-008a, MOD-008b, and MOD-008c, it is forbidden to move a model
out of the experimental stage/directory without the required CI tests.

**Rationale:**

The experimental stage allows rapid iteration without backward compatibility
constraints, enabling developers to refine APIs based on user feedback. This
protects users from unstable APIs while allowing innovation.

**Example:**

```python
# Good: Stage 1 - New experimental model
# File: physicsnemo/experimental/models/new_diffusion.py
class DiffusionModel(Module):
"""New diffusion model under active development. API may change."""
pass

# Good: After 1+ release cycles, promoted to production
# File: physicsnemo/models/diffusion.py (moved from experimental/)
class DiffusionModel(Module):
"""Stable diffusion model with backward compatibility guarantees."""
pass
```

**Anti-pattern:**

```python
# WRONG: New model directly in production folder
# File: physicsnemo/models/brand_new_model.py (should be in experimental/ first)
class BrandNewModel(Module):
"""Skipped experimental stage - risky for stability"""
pass
```
69 changes: 69 additions & 0 deletions .cursor/rules/mod-002b-add-deprecation-warnings-to-model.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
---
description: Model classes being deprecated must include deprecation warnings in both docstring and runtime, explaining why and what users should use instead, for at least 1 release cycle.
alwaysApply: false
---

When deprecating a model class, rule MOD-002b must be followed. Explicitly reference "Following rule MOD-002b, which requires adding deprecation warnings to both docstring and runtime..." when implementing deprecation.

## MOD-002b: Add deprecation warnings to deprecating model class

**Description:**

For a model class in the pre-deprecation stage in `physicsnemo/nn` or
`physicsnemo/models`, the developer should start planning its deprecation. This
is done by adding a warning message to the model class, indicating that the
model class is deprecated and will be removed in a future release.

The warning message should be a clear and concise message that explains why the
model class is being deprecated and what the user should do instead. The
deprecation message should be added to both the docstring and should be raised
at runtime. The developer is free to choose the mechanism to raise the
deprecation warning.

A model class cannot be deprecated without staying in the pre-deprecation stage
for at least 1 release cycle before it can be deleted from the codebase.

**Rationale:**

Ensures users have sufficient time to migrate to newer alternatives, preventing
breaking changes that could disrupt their workflows. This graduated approach
balances innovation with stability, a critical requirement for a scientific
computing framework.

**Example:**

```python
# Good: Pre-deprecation with warning
# File: physicsnemo/models/old_diffusion.py
class DiffusionModel(Module):
"""
Legacy diffusion model.

.. deprecated:: 0.5.0
``OldDiffusionModel`` is deprecated and will be removed in version 0.7.0.
Use :class:`~physicsnemo.models.NewDiffusionModel` instead.
"""
def __init__(self):
import warnings
warnings.warn(
"OldDiffusionModel is deprecated. Use NewDiffusionModel instead.",
DeprecationWarning,
stacklevel=2
)
super().__init__()
```

**Anti-pattern:**

```python
# WRONG: No deprecation warning in code
# File: physicsnemo/models/old_model.py
class OldModel(Module):
"""Will be removed next release.""" # Docstring mentions it but no runtime warning
def __init__(self):
# Missing: warnings.warn(..., DeprecationWarning)
super().__init__()

# WRONG: Deprecation without sufficient warning period
# (Model deprecated and removed in same release)
```
50 changes: 50 additions & 0 deletions .cursor/rules/mod-002c-remove-deprecated-model-from-codebase.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
---
description: After at least 1 release cycle in pre-deprecation stage with warnings, deprecated model classes can be deleted from the codebase.
alwaysApply: false
---

When removing deprecated models, rule MOD-002c must be followed. Explicitly reference "Following rule MOD-002c, which states that a model can only be deleted after at least 1 release cycle in pre-deprecation..." when removing code.

## MOD-002c: Remove deprecated model from codebase

**Description:**

After staying in the pre-deprecation stage (Stage 3) for at least 1 release
cycle, the model class is considered deprecated (Stage 4). It can then be
deleted from the codebase.

A model class cannot be deleted without first spending at least 1 release cycle
in the pre-deprecation stage with proper deprecation warnings (see MOD-002b).

**Rationale:**

This ensures users have sufficient warning and time to migrate their code to
newer alternatives. Premature deletion of models would break user code without
adequate notice, violating the framework's commitment to stability.

**Example:**

```python
# Good: Model spent 1 release cycle in pre-deprecation (v0.5.0 with warnings)
# Now in v0.6.0, can be deleted
# File: physicsnemo/models/old_diffusion.py - DELETED

# Release timeline:
# v0.5.0: Added deprecation warnings (Stage 3)
# v0.6.0: Model can be safely removed (Stage 4)
```

**Anti-pattern:**

```python
# WRONG: Deleting model without deprecation period
# v0.5.0: Model exists without warnings
# v0.6.0: Model deleted - BREAKS USER CODE!

# WRONG: Breaking changes in production without deprecation cycle
# File: physicsnemo/models/diffusion.py
class DiffusionModel(Module):
def __init__(self, new_required_param): # Breaking change!
# Changed API without deprecation warning - breaks user code
pass
```
Loading