Compare commits
17 Commits
8af3f80277
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 93a6d8683f | |||
| bd93399700 | |||
| 0887a9effa | |||
| 16327adb58 | |||
| f4711640da | |||
| 6efdb3e241 | |||
| dda8587dd8 | |||
| 3823372449 | |||
| 2f7924e580 | |||
| 35ca52fd84 | |||
| 878ca95eed | |||
| e0a75514b5 | |||
| 04c1dd53da | |||
| 4f92b12704 | |||
| 882b590865 | |||
| 74518e7daf | |||
| 2281ab1b27 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1 +1,5 @@
|
|||||||
*~
|
*~
|
||||||
|
*.log
|
||||||
|
flycheck_*.py
|
||||||
|
\#*#
|
||||||
|
test.pdf
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
# ad-calc
|
# ad-calc
|
||||||
|
|
||||||
Tools to help calculating values for Axiomatic Design analysis
|
Tools to help calculating values for Axiomatic Design analysis
|
||||||
|
|
||||||
|
`infocalc.py` calculates information content based upon a csv file or statistical parameters and upper/lower limits
|
||||||
|
|||||||
180
infocalc.py
Executable file
180
infocalc.py
Executable file
@@ -0,0 +1,180 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
## Axiomatic Design Information Calculator (and plotter)
|
||||||
|
## Author: Joseph Timothy foley <foley AT RU.IS>
|
||||||
|
## Start Date: 2026-02-27
|
||||||
|
## Input: data in csv file
|
||||||
|
## Output: information calculation and PDF for report/presentation
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
import argparse
|
||||||
|
from pathlib import PurePath##https://docs.python.org/3/library/pathlib.html#module-pathlib
|
||||||
|
import numpy as np
|
||||||
|
import matplotlib
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
from scipy.stats import norm,t
|
||||||
|
import scipy.stats
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
#Main program loop
|
||||||
|
print("""Axiomatic Design Information Calculator by Joseph. T. Foley<foley AT ru DOT is>
|
||||||
|
From https://gitea.cs.ru.is/AxiomaticDesign/adcalc/""")
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Axiomatic Design Information Calculator.")
|
||||||
|
subparsers = parser.add_subparsers(dest='mode')
|
||||||
|
subparsers.required = True
|
||||||
|
|
||||||
|
### MODE DATA
|
||||||
|
parser_data = subparsers.add_parser("DATA")
|
||||||
|
parser_data.add_argument('csvfile',
|
||||||
|
help="CSV file with data and headers")
|
||||||
|
parser_data.add_argument('column',
|
||||||
|
help='Which column header to take data from')
|
||||||
|
## MODE SIM
|
||||||
|
parser_sim = subparsers.add_parser("SIM")
|
||||||
|
parser_sim.add_argument('samplesize', type=int,
|
||||||
|
help="sample size")
|
||||||
|
parser_sim.add_argument('mean', type=float,
|
||||||
|
help="mean(average) value")
|
||||||
|
parser_sim.add_argument('stddev', type=float,
|
||||||
|
help="sample standard deviation")
|
||||||
|
## General Arguments
|
||||||
|
parser.add_argument('--lowerbound', type=float,
|
||||||
|
help='Tolerance low limit')
|
||||||
|
parser.add_argument('--upperbound', type=float,
|
||||||
|
help='Tolerance high limit')
|
||||||
|
parser.add_argument('--normalizey', action="store_true",
|
||||||
|
help='Set y-axis to normalized probability density')
|
||||||
|
parser.add_argument('--log', default="INFO",
|
||||||
|
help='Console log level: Number or DEBUG, INFO, WARNING, ERROR')
|
||||||
|
parser.add_argument('--legend', action="store_true",
|
||||||
|
help='Put legend on the PDF graph')
|
||||||
|
parser.add_argument('--graphinfo', action="store_true",
|
||||||
|
help='Put information on the PDF graph')
|
||||||
|
parser.add_argument('--xlabel',
|
||||||
|
help='X-axis label, if needed')
|
||||||
|
parser.add_argument('--outfile',
|
||||||
|
help="output graph to PDF file")
|
||||||
|
parser.add_argument('--fontsize', default=14, type=int,
|
||||||
|
help="Adjust font size")
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
## Set up logging
|
||||||
|
numeric_level = getattr(logging, args.log.upper(), None)
|
||||||
|
if not isinstance(numeric_level, int):
|
||||||
|
raise ValueError(f'Invalid log level: {args.log}')
|
||||||
|
#print(f"Log level: {numeric_level}")
|
||||||
|
logger = logging.getLogger("app")
|
||||||
|
logger.setLevel(numeric_level)
|
||||||
|
# log everything to file
|
||||||
|
logpath = "infocalc.log"
|
||||||
|
fh = logging.FileHandler(logpath)
|
||||||
|
fh.setLevel(logging.DEBUG)
|
||||||
|
# log to console
|
||||||
|
ch = logging.StreamHandler()
|
||||||
|
ch.setLevel(numeric_level)
|
||||||
|
# create formatter and add to handlers
|
||||||
|
consoleformatter = logging.Formatter('%(message)s')
|
||||||
|
ch.setFormatter(consoleformatter)
|
||||||
|
spamformatter = logging.Formatter('%(asctime)s %(name)s[%(levelname)s] %(message)s')
|
||||||
|
fh.setFormatter(spamformatter)
|
||||||
|
# add the handlers to logger
|
||||||
|
logger.addHandler(ch)
|
||||||
|
logger.addHandler(fh)
|
||||||
|
|
||||||
|
logger.debug("Creating infocalc log file %s", logpath)
|
||||||
|
|
||||||
|
# seed values for variable scoping
|
||||||
|
mean = 0
|
||||||
|
stddev = 1
|
||||||
|
samplesize =1
|
||||||
|
|
||||||
|
if args.mode == "DATA":
|
||||||
|
# filename pre-processing for output
|
||||||
|
inpath = PurePath(args.csvfile)
|
||||||
|
print(f"Input: {inpath}")
|
||||||
|
# grab the data and process
|
||||||
|
data = np.array(pd.read_csv(inpath)[args.column])
|
||||||
|
mean = data.mean()
|
||||||
|
stddev = data.std(ddof=1)
|
||||||
|
# Delta Degrees of Freedom: ddof=0 for population, ddof=1 for sample std dev
|
||||||
|
samplesize = len(data)
|
||||||
|
elif args.mode == "SIM":
|
||||||
|
mean = args.mean
|
||||||
|
stddev = args.stddev
|
||||||
|
samplesize = args.samplesize
|
||||||
|
df = samplesize - 1
|
||||||
|
|
||||||
|
prob = 0
|
||||||
|
if args.upperbound and args.lowerbound:
|
||||||
|
prob = t.cdf(df,args.upperbound, mean, stddev) - t.cdf(df,args.lowerbound, mean, stddev)
|
||||||
|
elif args.upperbound:
|
||||||
|
prob = t.cdf(df,args.upperbound, mean, stddev)
|
||||||
|
elif args.lowerbound:
|
||||||
|
prob = 1 - t.cdf(df,args.lowerbound, mean, stddev)
|
||||||
|
else:
|
||||||
|
prob = 1# no bounds set!
|
||||||
|
#print("probability: %f", prob)
|
||||||
|
info = -np.emath.log2(prob)
|
||||||
|
#print("information content: %f bits", info)
|
||||||
|
## set default fontsize
|
||||||
|
matplotlib.rcParams['font.size']=args.fontsize
|
||||||
|
## place text on plot: https://matplotlib.org/3.3.4/gallery/recipes/placing_text_boxes.html
|
||||||
|
fig, ax = plt.subplots()
|
||||||
|
if args.graphinfo:#put info on corner of graph
|
||||||
|
textstr = '\n'.join((
|
||||||
|
r'$n=%d$' % (samplesize),
|
||||||
|
r'$\mu=%.2f$' % (mean, ),
|
||||||
|
r'$\sigma=%.2f$' % (stddev, ),
|
||||||
|
r'$P=%.2f$' % (prob, ),
|
||||||
|
r'$I=%.2f$ bits' % (info, )))
|
||||||
|
# these are matplotlib.patch.Patch properties
|
||||||
|
props = dict(boxstyle='round', facecolor='wheat', alpha=0.5)
|
||||||
|
|
||||||
|
# place a text box in upper left in axes coords
|
||||||
|
ax.text(0.05, 0.95, textstr, transform=ax.transAxes, fontsize=args.fontsize,
|
||||||
|
verticalalignment='top', bbox=props)
|
||||||
|
xgraphlimits = {"min": mean-3*stddev, "max": mean+3*stddev}
|
||||||
|
if args.lowerbound and xgraphlimits["min"] > args.lowerbound:
|
||||||
|
xgraphlimits["min"] = args.lowerbound
|
||||||
|
if args.upperbound and xgraphlimits["max"] < args.upperbound:
|
||||||
|
xgraphlimits["max"] = args.upperbound
|
||||||
|
|
||||||
|
x = np.linspace(xgraphlimits["min"], xgraphlimits["max"], 500)
|
||||||
|
y = norm.pdf(x, loc=mean, scale=stddev)
|
||||||
|
if args.normalizey:
|
||||||
|
y = y * stddev#rescale back to unity area
|
||||||
|
plt.axvline(x=mean, color="green", linestyle="dashed", label="mean")
|
||||||
|
if args.lowerbound:
|
||||||
|
plt.axvline(args.lowerbound, color="red")
|
||||||
|
if args.upperbound:
|
||||||
|
plt.axvline(args.upperbound, color="red")
|
||||||
|
|
||||||
|
plt.plot(x, y, 'b-', label='Normal distribution')
|
||||||
|
#yt = scipy.stats.t.pdf(x, len(data)-1, mean, stddev)
|
||||||
|
#plt.plot(x, yt, 'g-', label='T Distribution')
|
||||||
|
|
||||||
|
# Filter for which region to fill
|
||||||
|
coloredregion = x#default fill all
|
||||||
|
if args.lowerbound and args.upperbound:
|
||||||
|
coloredregion = (x >= args.lowerbound) & ( x <= args.upperbound )
|
||||||
|
elif args.upperbound:
|
||||||
|
coloredregion = x <= args.upperbound
|
||||||
|
elif args.lowerbound:
|
||||||
|
coloredregion = x >= args.lowerbound
|
||||||
|
|
||||||
|
plt.fill_between(x, 0, y, where=coloredregion, color="grey", alpha=0.5, label="Design range",)
|
||||||
|
|
||||||
|
if args.xlabel:
|
||||||
|
plt.xlabel(args.xlabel)
|
||||||
|
plt.ylabel('Probability density')
|
||||||
|
if args.legend:
|
||||||
|
plt.legend()
|
||||||
|
#plt.grid(True)
|
||||||
|
top = plt.ylim()[1]
|
||||||
|
|
||||||
|
if args.outfile:
|
||||||
|
logger.info(f"Graph output to {args.outfile}")
|
||||||
|
plt.savefig(args.outfile,bbox_inches='tight')
|
||||||
|
else:
|
||||||
|
plt.show()
|
||||||
56
normdist.py
56
normdist.py
@@ -1,56 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
import numpy as np
|
|
||||||
import matplotlib.pyplot as plt
|
|
||||||
from scipy.stats import norm
|
|
||||||
|
|
||||||
## Data goes here for now --foley
|
|
||||||
data = np.array([1, 1.1, 0.9, 1, 1, 0.9, 0.9])
|
|
||||||
lowerbound = 0.9
|
|
||||||
upperbound = 1.0
|
|
||||||
|
|
||||||
mean = data.mean()
|
|
||||||
stddev = data.std(ddof=1)
|
|
||||||
# Delta Degrees of Freedom: ddof=0 for population, ddof=1 for sample std dev
|
|
||||||
prob = norm.cdf(upperbound, mean, stddev) - norm.cdf(lowerbound, mean, stddev)
|
|
||||||
#print("probability: %f", prob)
|
|
||||||
info = -np.emath.log2(prob)
|
|
||||||
#print("information content: %f bits", info)
|
|
||||||
## place text on plot: https://matplotlib.org/3.3.4/gallery/recipes/placing_text_boxes.html
|
|
||||||
fig, ax = plt.subplots()
|
|
||||||
textstr = '\n'.join((
|
|
||||||
r'$n=%d$' % (len(data)),
|
|
||||||
r'$\mu=%.2f$' % (mean, ),
|
|
||||||
r'$P=%.2f$' % (prob, ),
|
|
||||||
r'$I=%.2f$' % (info, )))
|
|
||||||
# these are matplotlib.patch.Patch properties
|
|
||||||
props = dict(boxstyle='round', facecolor='wheat', alpha=0.5)
|
|
||||||
|
|
||||||
# place a text box in upper left in axes coords
|
|
||||||
ax.text(0.05, 0.95, textstr, transform=ax.transAxes, fontsize=14,
|
|
||||||
verticalalignment='top', bbox=props)
|
|
||||||
|
|
||||||
x = np.linspace(mean-3*stddev, mean+3*stddev, 500)
|
|
||||||
y = norm.pdf(x, mean, stddev)
|
|
||||||
plt.axvline(x=mean, color="green", linestyle="dashed", label="mean")
|
|
||||||
plt.axvline(lowerbound, color="red")
|
|
||||||
plt.axvline(upperbound, color="red")
|
|
||||||
|
|
||||||
plt.plot(x, y, 'b-', label='Normal distribution')
|
|
||||||
#yt = scipy.stats.t.pdf(x, len(data)-1, mean, stddev)
|
|
||||||
#plt.plot(x, yt, 'g-', label='T Distribution')
|
|
||||||
coloredregion = (x >= lowerbound) & ( x <= upperbound ) #select x values
|
|
||||||
plt.fill_between(x, 0, y, where=coloredregion, color="grey", alpha=0.5, label="Design range")
|
|
||||||
plt.xlabel('X')
|
|
||||||
plt.ylabel('Probability density')
|
|
||||||
plt.legend()
|
|
||||||
plt.grid(True)
|
|
||||||
|
|
||||||
top = plt.ylim()[1]
|
|
||||||
|
|
||||||
|
|
||||||
plt.show()
|
|
||||||
# annotate values on X after drawing the graphs
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
import pandas as pd
|
|
||||||
|
|
||||||
df = pd.DataFrame(
|
|
||||||
{
|
|
||||||
"Name": [
|
|
||||||
"Braund, Mr. Owen Harris",
|
|
||||||
"Allen, Mr. William Hentry",
|
|
||||||
"Bonnell, Miss. Elizabeth",
|
|
||||||
],
|
|
||||||
"Age": [22, 35, 58],
|
|
||||||
"Sex": ["male", "male", "female"],
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
print(df.describe())
|
|
||||||
|
|
||||||
titanic = pd.read_csv("titanic.csv")
|
|
||||||
print(titanic.head(8))
|
|
||||||
titanic.to_excel("titanic.xlsx", sheet_name="passengers", index=False)
|
|
||||||
titanic_xltest = pd.read_excel("titanic.xlsx", sheet_name="passengers")
|
|
||||||
print("INFO")
|
|
||||||
print(titanic.info())
|
|
||||||
8
testdata.csv
Normal file
8
testdata.csv
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
data1,data2,
|
||||||
|
1.0,1.1,
|
||||||
|
1.1,1.2,
|
||||||
|
0.9,1.3,
|
||||||
|
1.0,1.4,
|
||||||
|
1.0,1.5,
|
||||||
|
0.9,1.6,
|
||||||
|
0.9,1.7,
|
||||||
|
6
tests.sh
Executable file
6
tests.sh
Executable file
@@ -0,0 +1,6 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Get infocalc.py from https://gitea.cs.ru.is/AxiomaticDesign/adcalc
|
||||||
|
echo "Loading data from file"
|
||||||
|
./infocalc.py --lowerbound 0.9 --upperbound 1.1 --graphinfo DATA testdata.csv data1
|
||||||
|
echo "Creating simulated curve from parameters"
|
||||||
|
./infocalc.py --lowerbound 0.9 --upperbound 1.1 SIM 8 1.0 0.5
|
||||||
11
toy example/toy_hybrid.saddl
Normal file
11
toy example/toy_hybrid.saddl
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
DEFINE CN1 as 8-year-old child can operate
|
||||||
|
DEFINE CN2 as Flies 3 m up
|
||||||
|
DEFINE CN3 as Flies at least 5 m from 1 m starting height
|
||||||
|
DEFINE CN4 as Doesn't injur people
|
||||||
|
DEFINE CN5 as Doesn't shatter glass windows
|
||||||
|
DEFINE CN6 as Fuel is easily available in common households
|
||||||
|
DEFINE FR1 as When shot up vertically, the "rocket" flies at least 3m
|
||||||
|
DEFINE FR2 as When shot horizontally from an elevation of 1 m, the "rocket" travels at least 5 m
|
||||||
|
DEFINE FR3 as The maximum kinetic energy at launch is less than 0.5 J
|
||||||
|
DEFINE FR4 as The "rocket's" impact area is at least 30 mm²
|
||||||
|
DEFINE DP1 as Hybrid launcher with rocket non-propelled
|
||||||
11
toy example/toy_rocket.saddl
Normal file
11
toy example/toy_rocket.saddl
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
DEFINE CN1 as 8-year-old child can operate
|
||||||
|
DEFINE CN2 as Flies 3 m up
|
||||||
|
DEFINE CN3 as Flies at least 5 m from 1 m starting height
|
||||||
|
DEFINE CN4 as Doesn't injur people
|
||||||
|
DEFINE CN5 as Doesn't shatter glass windows
|
||||||
|
DEFINE CN6 as Fuel is easily available in common households
|
||||||
|
DEFINE FR1 as When shot up vertically, the "rocket" flies at least 3m
|
||||||
|
DEFINE FR2 as When shot horizontally from an elevation of 1 m, the "rocket" travels at least 5 m
|
||||||
|
DEFINE FR3 as The maximum kinetic energy at launch is less than 0.5 J
|
||||||
|
DEFINE FR4 as The "rocket's" impact area is at least 30 mm²
|
||||||
|
DEFINE DP1 as Kinetic-reaction self-launch
|
||||||
11
toy example/toy_slingshot.saddl
Normal file
11
toy example/toy_slingshot.saddl
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
DEFINE CN1 as 8-year-old child can operate
|
||||||
|
DEFINE CN2 as Flies 3 m up
|
||||||
|
DEFINE CN3 as Flies at least 5 m from 1 m starting height
|
||||||
|
DEFINE CN4 as Doesn't injur people
|
||||||
|
DEFINE CN5 as Doesn't shatter glass windows
|
||||||
|
DEFINE CN6 as Fuel is easily available in common households
|
||||||
|
DEFINE FR1 as When shot up vertically, the "rocket" flies at least 3m
|
||||||
|
DEFINE FR2 as When shot horizontally from an elevation of 1 m, the "rocket" travels at least 5 m
|
||||||
|
DEFINE FR3 as The maximum kinetic energy at launch is less than 0.5 J
|
||||||
|
DEFINE FR4 as The "rocket's" impact area is at least 30 mm²
|
||||||
|
DEFINE DP1 as Slingshot launcher
|
||||||
Reference in New Issue
Block a user