Skip to content

Commit 9d87b17

Browse files
authored
Merge pull request #18 from smdogroup/flag_removal
Relocation for analyzed flags & paper modifications
2 parents c338772 + f75b6f6 commit 9d87b17

25 files changed

+236
-320
lines changed

.github/workflows/draft-pdf.yml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
name: Draft PDF
2+
on: [push]
3+
4+
jobs:
5+
paper:
6+
runs-on: ubuntu-latest
7+
name: Paper Draft
8+
steps:
9+
- name: Checkout
10+
uses: actions/checkout@v4
11+
- name: Build draft PDF
12+
uses: openjournals/openjournals-draft-action@master
13+
with:
14+
journal: joss
15+
# This should be the path to the paper within your repo.
16+
paper-path: joss/paper.md
17+
- name: Upload
18+
uses: actions/upload-artifact@v4
19+
with:
20+
name: paper
21+
# This is the output path where Pandoc will write the compiled
22+
# PDF. Note, this should be the same directory as the input
23+
# paper.md
24+
path: joss/paper.pdf

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ source venv/bin/activate
2222
Then, with the virtual environment activated, simply install the package from PyPi with
2323

2424
```
25-
pip install flume-smdo==1.0.2
25+
pip install flume-smdo
2626
```
2727

2828
This will install the base classes, optimizer interfaces, and utilities into a Python package named "flume". For those who want to make changes to the code or access the unit tests and examples, you can also clone the repository and then install in editable mode.

docs/source/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ source venv/bin/activate
1818
Then, with the virtual environment activated, simply install the package from PyPi with
1919

2020
```
21-
pip install flume-smdo==1.0.2
21+
pip install flume-smdo
2222
```
2323

2424
This will install the base classes, optimizer interfaces, and utilities into a Python package named "flume". For those who want to make changes to the code or access the unit tests and examples, you can also clone the repository and then install in editable mode.

docs/source/base_class_reference.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
Base Class Reference
2-
--------------------
1+
Primary Class Reference
2+
-----------------------
33

4-
This page outlines the three base classes for *Flume*: *Analysis*, *State*, and *System*.
4+
This page outlines the three primary classes for *Flume*: *Analysis*, *State*, and *System*.
55

66
.. automodule:: flume.base_classes.analysis
77
:members:

docs/source/flume_overview.ipynb

Lines changed: 14 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,22 @@
1313
"- Assembly of a *System* with specific instances of *Analysis* classes\n",
1414
"- Formulating an optimization problem with a *Flume* optimizer interface and executing the optimization script\n",
1515
"\n",
16-
"To address these points, this notebook will provide sections of code with accompanying descriptions regarding the use of the various base classes and how to set up an optimization problem.\n",
16+
"To address these points, this notebook will provide sections of code with accompanying descriptions regarding the use of the primary classes and how to set up an optimization problem.\n",
1717
"\n",
1818
"## Framework Architecture\n",
1919
"\n",
20-
"*Flume* defines three base classes, which are the key aspects that define the framework's architecture. A brief summary of each is provided below. \n",
20+
"*Flume* defines three primary classes, which are the key aspects that define the framework's architecture. A brief summary of each is provided below. \n",
2121
"\n",
22-
"- *State*: The base class that is used to wrap variables and outputs. Each state object comprises a numeric value, a string description, a derivative value, and source information. The numeric value can be a float or NumPy array, and the type and shape information are ascertained from the provided value. The source information denotes the *Analysis* object that the *State* data is associated with. When creating a *State*, the user should provide the value `value`, description `desc`, and source `source` information. The source is always set to be `self`, which links the *State* to a specific *Analysis* object. The derivative value `deriv` is updated within the adjoint analysis and is not provided when the *State* is created.\n",
23-
"- *Analysis*: The base class that defines the structure for analysis disciplines within the framework. It establishes a set of common methods, such as setting variable values, extracting outputs, and adjoint tests, that are utilized to define the structure of the framework. Internally, this also establishes the connections between *Analysis* classes that denote a transfer of information. All classes that inherit from the *Analysis* base class must have arguments for the object's unique name `obj_name`, a list of sub-analyses `sub_analyses`, and keyword arguments. This will be explained in context below for the Rosenbrock example.\n",
24-
"- *System*: The base class that wraps multiple *Analysis* objects into a system, which can then be utilized to perform optimization using one of *Flume*'s optimizer interfaces. When constructing a *System* object, the user must provide a name for the system `sys_name` and define a list of top-level *Analysis* objects `top_level_analysis_list`. This list contains the *Analysis* objects that define output *State* objects that will be used for defining the objective and constraint functions that characterize the optimization problem. The user can optionally provide arguments for `log_name` and `log_prefix`, which specify the file name to use for the log file and the output directory for file management.\n",
22+
"- *State*: The class that is used to wrap variables and outputs. Each state object comprises a numeric value, a string description, a derivative value, and source information. The numeric value can be a float or NumPy array, and the type and shape information are ascertained from the provided value. The source information denotes the *Analysis* object that the *State* data is associated with. When creating a *State*, the user should provide the value `value`, description `desc`, and source `source` information. The source is always set to be `self`, which links the *State* to a specific *Analysis* object. The derivative value `deriv` is updated within the adjoint analysis and is not provided when the *State* is created.\n",
23+
"- *Analysis*: The abstract base class that defines the structure for analysis disciplines within the framework. It establishes a set of common methods, such as setting variable values, extracting outputs, and adjoint tests, that are utilized to define the structure of the framework. Internally, this also establishes the connections between *Analysis* classes that denote a transfer of information. All classes that inherit from the *Analysis* base class must have arguments for the object's unique name `obj_name`, a list of sub-analyses `sub_analyses`, and keyword arguments. This will be explained in context below for the Rosenbrock example.\n",
24+
"- *System*: The class that serves as a container to specify an analysis sequence using *Analysis* objects, which can then be utilized to perform optimization using one of *Flume*'s optimizer interfaces. When constructing a *System* object, the user must provide a name for the system `sys_name` and define a list of top-level *Analysis* objects `top_level_analysis_list`. This list contains the *Analysis* objects that define output *State* objects that will be used for defining the objective and constraint functions that characterize the optimization problem. The user can optionally provide arguments for `log_name` and `log_prefix`, which specify the file name to use for the log file and the output directory for file management.\n",
2525
"\n",
2626
"The structure of *Flume* is visualized in the image below, which depicts an abstracted *System* that encapsulates four distinct _Analysis_ objects.\n",
2727
"Arrows that link _Analysis_ objects denote _State_ objects that connect outputs of one discipline to variables of another.\n",
2828
"_Analysis_ objects that are outlined in red and are labeled with \"Top-level **_Analysis_** Object\" are those that define output _States_ which are utilized for optimization.\n",
2929
"Thus, the arrows that extend beyond the _System_ boundary are _States_ that define design variables, the objective function, or constraint functions for an optimization problem that is wrapped within the framework.\n",
3030
"\n",
31-
"![Abstracted *System* that illustrates the structure of the framework. Here, *State*, *Analysis*, and *System* are emphasized to denote the use of the base classes provided within the library. Arrows extending beyond the boundary of the *System* denote quantities that are utilized for numerical optimization.](Images/Flume_DAG_Diagram.svg)\n",
31+
"![Abstracted *System* that illustrates the structure of the framework. Here, *State*, *Analysis*, and *System* are emphasized to denote the use of the primary classes provided within the library. Arrows extending beyond the boundary of the *System* denote quantities that are utilized for numerical optimization.](Images/Flume_DAG_Diagram.svg)\n",
3232
"\n",
3333
"## Framework Nomenclature\n",
3434
"Before including any code, it is important to clarify the nomenclature that is used within the framework. All *Analysis* classes contain at least one variable and output, and they may optionally include parameters. In the context of *Flume*, these are defined as follows.\n",
@@ -91,9 +91,6 @@
9191
" # Compute the value of the Rosenbrock function\n",
9292
" f = (a - x) ** 2 + b * (y - x**2) ** 2\n",
9393
"\n",
94-
" # Update the analyzed attribute\n",
95-
" self.analyzed = True\n",
96-
"\n",
9794
" # Store the outputs\n",
9895
" self.outputs = {}\n",
9996
"\n",
@@ -125,9 +122,6 @@
125122
" # Compute yb\n",
126123
" yb += (2 * b * (y - x**2)) * fb\n",
127124
"\n",
128-
" # Update the analyzed adjoint attribute\n",
129-
" self.adjoint_analyzed = True\n",
130-
"\n",
131125
" # Assign the derivative values\n",
132126
" self.variables[\"x\"].set_deriv_value(deriv_val=xb)\n",
133127
" self.variables[\"y\"].set_deriv_value(deriv_val=yb)\n",
@@ -173,7 +167,7 @@
173167
"source": [
174168
"### `_analyze` Method\n",
175169
"\n",
176-
"The primary intent of this method is to compute the outputs of interest using the input values for the parameters and variables. To do so, the variable values and parameters are extracted with\n",
170+
"For the _Analysis_ base class, the \"Template Method\" behavioral design pattern is used (see _Design Patterns: Elements of Reusable Object-Oriented Software_ by Gamma et al. for additional details). This means that the user is responsible for defining the private, helper functions, such as `_analyze`, that define the internal hooks used by other methods defined in the _Analysis_ base class. For the `_analyze` method, its intent is to compute the outputs of interest using the input values for the parameters and variables. To do so, the variable values and parameters are extracted with\n",
177171
"```python\n",
178172
"# Extract the variable values\n",
179173
"x = self.variables[\"x\"].value\n",
@@ -187,11 +181,7 @@
187181
"```python\n",
188182
"f = (a - x) ** 2 + b * (y - x**2) ** 2\n",
189183
"```\n",
190-
"After the outputs are computed, the user should update the `analyzed` attribute to denote that computations have concluded for the current *Analysis*. \n",
191-
"```python\n",
192-
"self.analyzed = True\n",
193-
"```\n",
194-
"This is used internally to avoid recomputing data if an *Analysis* appears multiple times within a *System* before variable values are updated. Then, the outputs are stored within the `outputs` dictionary, where the values are again wrapped in *State* objects.\n",
184+
"Then, the outputs are stored within the `outputs` dictionary, where the values are again wrapped in *State* objects.\n",
195185
"```python\n",
196186
"# Store the outputs\n",
197187
"self.outputs = {}\n",
@@ -236,12 +226,7 @@
236226
"# Compute contributions to yb\n",
237227
"yb += (2 * b * (y - x**2)) * fb\n",
238228
"```\n",
239-
"After computing the derivatives, the user should update the `adjoint_analyzed` attribute to denote that computations for the derivatives have concluded, similar to the behavior for the `_analyze` method.\n",
240-
"```python\n",
241-
"# Update the analyzed adjoint attribute\n",
242-
"self.adjoint_analyzed = True\n",
243-
"```\n",
244-
"Finally, the derivative values are assigned to the *State* objects stored within the `variables` dictionary, which ensures that these quantities are stored before concluding the current `_analyze_adjoint` method.\n",
229+
"After computing the derivatives the derivative values are assigned to the *State* objects stored within the `variables` dictionary, which ensures that these quantities are stored before concluding the current `_analyze_adjoint` method.\n",
245230
"```python\n",
246231
"# Assign the derivative values\n",
247232
"self.variables[\"x\"].set_deriv_value(deriv_val=xb)\n",
@@ -383,9 +368,6 @@
383368
" x_dv = self.variables[\"x_dv\"].value\n",
384369
" y_dv = self.variables[\"y_dv\"].value\n",
385370
"\n",
386-
" # Update the analyzed attribute\n",
387-
" self.analyzed = True\n",
388-
"\n",
389371
" # Store the outputs in the outputs dictionary\n",
390372
" self.outputs = {}\n",
391373
"\n",
@@ -412,9 +394,6 @@
412394
" x_dvb += xb\n",
413395
" y_dvb += yb\n",
414396
"\n",
415-
" # Update the analyzed adjoint attribute\n",
416-
" self.adjoint_analyzed = True\n",
417-
"\n",
418397
" # Set the derivative values\n",
419398
" self.variables[\"x_dv\"].set_deriv_value(deriv_val=x_dvb)\n",
420399
"\n",
@@ -526,9 +505,6 @@
526505
" # Compute the value of the constraint\n",
527506
" g = x**2 + y**2\n",
528507
"\n",
529-
" # Update the analyzed attribute\n",
530-
" self.analyzed = True\n",
531-
"\n",
532508
" # Store the outputs in the outputs dictionary\n",
533509
" self.outputs = {}\n",
534510
"\n",
@@ -560,9 +536,6 @@
560536
" xb += gb * 2 * x\n",
561537
" yb += gb * 2 * y\n",
562538
"\n",
563-
" # Update the analyzed adjoint attribute\n",
564-
" self.adjoint_analyzed = True\n",
565-
"\n",
566539
" # Assign the derivative values\n",
567540
" self.variables[\"x\"].set_deriv_value(deriv_val=xb)\n",
568541
" self.variables[\"y\"].set_deriv_value(deriv_val=yb)\n",
@@ -695,12 +668,12 @@
695668
" message: Optimization terminated successfully\n",
696669
" success: True\n",
697670
" status: 0\n",
698-
" fun: 0.04567480871937527\n",
671+
" fun: 0.04567480871950009\n",
699672
" x: [ 7.864e-01 6.177e-01]\n",
700-
" nit: 9\n",
673+
" nit: 18\n",
701674
" jac: [-1.911e-01 -1.501e-01]\n",
702-
" nfev: 12\n",
703-
" njev: 9\n"
675+
" nfev: 26\n",
676+
" njev: 18\n"
704677
]
705678
},
706679
{
@@ -798,7 +771,7 @@
798771
],
799772
"metadata": {
800773
"kernelspec": {
801-
"display_name": "venv",
774+
"display_name": "venv (3.10.8)",
802775
"language": "python",
803776
"name": "python3"
804777
},

examples/cantilever_beam/compliance.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,6 @@ def _analyze(self):
6868
# Compute the compliance
6969
c = np.dot(f, u)
7070

71-
# Update the analyzed attribute
72-
self.analyzed = True
73-
7471
# Store the outputs
7572
self.outputs = {}
7673

@@ -93,9 +90,6 @@ def _analyze_adjoint(self):
9390
# Compute the contributions to db
9491
db[0:-2] += cb * f
9592

96-
# Update the analyzed adjoint attribute
97-
self.adjoint_analyzed = True
98-
9993
# Assign the derivative values
10094
self.variables["d"].set_deriv_value(db)
10195

examples/cantilever_beam/independents.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,6 @@ def _analyze(self):
4848
# Extract the variables
4949
h_dv = self.variables["h_dv"].value
5050

51-
# Update the analyzed attribute
52-
self.analyzed = True
53-
5451
# Store the outputs
5552
self.outputs = {}
5653

@@ -74,9 +71,6 @@ def _analyze_adjoint(self):
7471
# Update the derivatives for h_dv
7572
h_dvb += hb
7673

77-
# Update the analyzed adjoint attribute
78-
self.adjoint_analyzed = True
79-
8074
# Assign the derivative values
8175
self.variables["h_dv"].set_deriv_value(h_dvb)
8276

examples/cantilever_beam/inertia.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,6 @@ def _analyze(self):
5959
# Compute the moment of inertia for each element (square cross section assumed)
6060
I = 1 / 12 * b * h**3
6161

62-
# Update the analyzed attribute
63-
self.analyzed = True
64-
6562
# Store the outputs
6663
self.outputs = {}
6764

@@ -89,9 +86,6 @@ def _analyze_adjoint(self):
8986
# Compute the contribution to hb from Ib
9087
hb += Ib * 1 / 12 * b * 3 * h**2
9188

92-
# Update the analyzed adjoint attribute
93-
self.adjoint_analyzed = True
94-
9589
# Assign the derivative values
9690
self.variables["h"].set_deriv_value(deriv_val=hb)
9791

examples/cantilever_beam/linear_solve.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,6 @@ def _analyze(self):
8484
d = self.lu.solve(self.f)
8585
self.d = d
8686

87-
# Update the analyzed attribute
88-
self.analyzed = True
89-
9087
# Store the outputs
9188
self.outputs = {}
9289

@@ -177,9 +174,6 @@ def _analyze_adjoint(self):
177174
# Compute the contributions for the current element
178175
Ib[idx] += psi_e.T @ (self.Ke_by_I @ d_e)
179176

180-
# Update the analyzed adjoint attribute
181-
self.adjoint_analyzed = True
182-
183177
# Assign the derivative values
184178
self.variables["I"].set_deriv_value(Ib)
185179

examples/cantilever_beam/volume.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,6 @@ def _analyze(self):
6969
# Sum the element volumes to get the total volume
7070
V = np.sum(Ve)
7171

72-
# Update the analyzed attribute
73-
self.analyzed = True
74-
7572
# Store the outputs
7673
self.outputs = {}
7774

@@ -94,9 +91,6 @@ def _analyze_adjoint(self):
9491
# Compute the contributions to hb from Vb
9592
hb += Vb * b * self.Le
9693

97-
# Update the analyzed adjoint attribute
98-
self.adjoint_analyzed = True
99-
10094
# Assign the derivative values
10195
self.variables["h"].set_deriv_value(deriv_val=hb)
10296

0 commit comments

Comments
 (0)